주 콘텐츠로 건너뛰기

AQC + Trotter 해밀토니안 동역학을 위한 Qiskit Function 템플릿 배포 및 실행

개요

이것은 해밀토니안 동역학을 위한 실험에 구애받지 않는 Qiskit Function 템플릿입니다. 1D 최근접 이웃 파울리 해밀토니안, 준비된 초기 상태(선택 사항), 그리고 observable 세트가 주어지면, Trotter 시간 진화, 근사 양자 컴파일(AQC) Circuit 압축, 완화된 실행을 수행한 후 각 observable의 시계열을 반환합니다. 설정(PRE)과 분석(POST)을 바꾸면 동일한 핵심으로 다른 실험을 수행할 수 있습니다:

PRE (사용자 설정)FUNCTION (여기에 배포됨)POST (사용자 분석)
선택적 로컬 킥과 함께 Circuit 또는 곱 상태로 상태 준비Trotter 합성 → AQC 압축 → statevector, fake, runtime에서의 실행, O(t)\langle O \rangle(t) 반환중성자 산란을 위한 S(q,ω)S(q, \omega), 또는 자화, 수송, 켄치 동역학 등

이 템플릿은 다른 애플리케이션 템플릿과 함께 Qiskit Function 템플릿 저장소에 게시되어 있습니다. 이 노트북은 이를 여러분 자신의 Qiskit Serverless 계정에 배포합니다. 한 번 실행하면, 이후 모든 노트북은 serverless.load("aqc-dynamics-function")으로 이 함수를 호출할 수 있습니다.

실제 과학 응용 예제는 AQC + Trotter 동역학 Serverless 워크플로로 중성자 산란 시뮬레이션하기를 참조하세요. 해당 튜토리얼은 이 함수를 호출해 KCuF3_3의 동적 구조 인자를 계산합니다. 반면 이 노트북은 배포와 입력 계약을 다룹니다.

요구 사항

시작하기 전에, 이 노트북의 커널 환경에 다음이 있는지 확인하세요:

  • Qiskit SDK v2.0 이상(pip install qiskit).

  • Qiskit Serverless에서 워크로드를 배포하고 실행하는 Qiskit IBM Catalog 클라이언트(pip install qiskit-ibm-catalog).

함수 자체의 과학적 종속성(qiskit-addon-aqc-tensor, cotengrust, qiskit-aer)은 로컬에 설치할 필요가 없습니다.

템플릿 소스 파일 가져오기

이 함수는 Qiskit Serverless가 클라우드에서 실행하는 작은 Python 패키지이므로, 그 소스는 배포 시 업로드되는 로컬 파일로 존재해야 합니다. 이 패키지는 Qiskit Function 템플릿 저장소에 게시되어 있습니다.

source_files 다운로드

다운로드는 저장소 내 디렉토리의 전체 경로 이름을 딴 단일 zip 파일입니다:

qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip

  1. 이 노트북이 있는 디렉토리에 압축을 풉니다.

  2. 압축 해제된 폴더의 긴 이름을 source_files로 바꿉니다.

이제 작업 디렉토리는 다음과 같이 보입니다:

your-working-directory/
├── function-template-aqc-trotter.ipynb <- this notebook
└── source_files/ <- the renamed folder
├── __init__.py
├── program.py
└── source/
├── __init__.py
├── _serverless.py
├── app_function.py
├── aqc.py
├── build.py
├── execute.py
└── hamiltonian.py

이름은 정확히 source_files여야 합니다. 이것이 3단계에서 업로드하는 working_dir이기 때문입니다.

program.py는 게이트웨이가 호출하는 진입점입니다. source/ 아래의 모든 것은 해밀토니안 및 Trotter 합성, AQC 압축, 실행 단계별로 나뉜 구현입니다. 다음에 나오는 예제를 실행하기 위해 이 중 어느 것도 편집할 필요가 없습니다. 3단계는 전체 디렉토리를 업로드하므로, 파일을 변경할 때마다 그 단계를 반복하세요.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog

1. 인증

qiskit-ibm-catalog를 사용하여 API 키(토큰)와 CRN(인스턴스)으로 QiskitServerless에 인증합니다. 이는 IBM Quantum® Platform 대시보드에서 찾을 수 있습니다. 이러한 자격 증명을 사용하면 선택한 함수를 업로드하거나 실행하기 위해 로컬에서 serverless 클라이언트를 인스턴스화할 수 있습니다:

from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

선택적으로 save_account()를 사용하여 자격 증명을 로컬 환경에 저장할 수 있습니다(IBM Cloud® 계정 설정 가이드 참조). 이는 QiskitRuntimeService.save_account()와 동일한 파일에 자격 증명을 기록한다는 점에 유의하세요:

QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

계정이 저장되어 있으면 인증을 위해 토큰을 제공할 필요가 없습니다:

from qiskit_ibm_catalog import QiskitServerless

# Authenticate to the remote cluster
# In this case, loading a saved account
serverless = QiskitServerless()

# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT
# serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

2. 종속성 선언

관리형 기본 serverless 이미지 외에 함수가 필요로 하는 패키지입니다.

참고

게이트웨이는 허용 목록(requirements-dynamic-dependencies.txt)에 있는 이름만 설치하며, 패키지 이름으로 매칭되고 ==로 허용된 버전에 고정됩니다. 그 외의 것은 (허용 목록에 있는 패키지의 종속성으로서) 전이적으로 들어와야 합니다. [extras] 문법도 지원됩니다: qiskit-addon-aqc-tensor[quimb-jax]quimbjax를 설치하는 것입니다. cotengrust는 텐서 네트워크 시뮬레이션 중 메모리 효율성을 위해 필요합니다. qiskit-aerfake Backend(로컬 노이즈 시뮬레이션)를 위해 별도로 나열됩니다.

DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]

3. 함수 정의 및 업로드

from qiskit_ibm_catalog import QiskitFunction

fn = QiskitFunction(
title="aqc-dynamics-function",
entrypoint="program.py",
working_dir="source_files/",
dependencies=DEPENDENCIES,
)
serverless.upload(fn)
QiskitFunction(aqc-dynamics-function)

4. 등록되었는지 확인

next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)

함수 참조

이것은 간단한 소개입니다. 모든 필드는 AQC Dynamics 템플릿 README에 완전히 문서화되어 있습니다: 검증 규칙을 포함한 전체 입력 표, 출력 필드, 실행 Backend, 그리고 추가적인 예제입니다. 다음은 이후 예제를 읽는 데 충분한 짧은 버전입니다.

입력

모든 실행은 단일 fn.run(...) 호출입니다. 표에서 처음 세 가지 입력인 hamiltonian, t_steps, aqc_segments만 필수입니다. 이후의 모든 것은 선택 사항이며 표시된 기본값으로 대체되므로, 최소한의 호출은 세 개의 인수이며 표의 나머지는 선택적으로 사용할 수 있는 기능입니다. 해밀토니안의 num_qubits가 체인 길이를 설정하므로 별도의 크기 입력은 없습니다.

입력기본값설명
hamiltonian필수SparsePauliOp로서의 1D 최근접 이웃 파울리 해밀토니안입니다. 문자열은 파울리 연산자이므로 암묵적인 2분의 1 계수는 없습니다.
t_steps필수총 Trotter 스텝 수입니다. T = t_steps * dt까지 진화하며 각 t_k = k * dt에서 모든 observable을 보고합니다.
aqc_segments필수압축 계획: {"n_steps": k, "ansatz_steps": m} 목록입니다. sum(n_steps) 스텝이 압축되며, 나머지는 일반 Trotter로 실행됩니다.
dt0.2하나의 Trotter 스텝에서 진행되는 물리적 시간입니다.
initial_state|0...0>진화시킬 준비된 QuantumCircuit입니다. 로컬 킥이 있다면 이 Circuit에 포함시키세요.
observables사이트별 ZEstimatorV2observables 인수로 받아들이는 모든 것입니다. 출력 열당 하나의 observable입니다.
trotter_options2차 Suzuki{"method": ..., "synthesis_settings": {...}}. repstime은 함수가 소유합니다.
aqc_options설명 참조max_bond(32), cutoff(1e-8), autodiff_backend("jax"), fidelity_target(None), optimizer_settings(L-BFGS-B, jac=True, maxiter=300).
estimator_optionsDD, 트월링, TREXEstimatorV2.options, 그대로 전달됩니다. 제공된 딕셔너리는 병합되지 않고 기본값을 완전히 대체합니다.
transpiler_options{"optimization_level": 3}generate_preset_pass_manager 키워드 인수입니다. 실행 경로가 이를 소유하므로 backendtarget은 거부됩니다.
backend"runtime""statevector", "fake", 또는 "runtime".
backend_name가장 한가함runtime을 위한 IBM® Backend 이름, 또는 이름이 지정된 페이크 Backend입니다.
batches1Circuit을 N개의 runtime 작업으로 분할합니다. 하나의 배치는 단일 작업을 제출하며 세션을 생성하지 않습니다.
parallel_simFalseRay를 사용하여 사용 가능한 모든 코어에 로컬 시뮬레이터 경로를 분산시킵니다. runtime에는 영향을 주지 않습니다.
return_circuitsFalseobservable 시계열과 함께 결과에 논리적 AQC + Trotter Circuit을 반환합니다.

실행 Backend

세 경로는 모두 동일한 코드와 동일한 완화 설정을 공유합니다. 이들은 Circuit이 실행되는 위치에서만 차이가 있습니다.

backend무엇인가자격 증명참고 사항
"statevector"정확한 StatevectorEstimatorServerless 계정만 필요정확한 참조 경로입니다. QPU 시간이 필요 없습니다.
"fake"Qiskit 페이크 Backend에서의 노이즈 로컬 시뮬레이션Serverless 계정만 필요완화된 runtime 경로의 충실한 예행연습입니다. qiskit-aer가 필요합니다. 기본값은 127-Qubit fake_sherbrooke입니다.
"runtime"(기본값)실제 QPU에 대한 완화된 EstimatorV2Serverless 계정 및 QPU 접근 권한이 있는 인스턴스backend_name은 선택 사항이며, 생략하면 가장 한가한 디바이스가 선택됩니다.

두 시뮬레이터 경로 모두 배포된 함수를 여전히 호출하므로, QPU 시간을 사용하지 않더라도 저장된 Serverless 계정이 필요합니다. 이어지는 두 예제는 동일한 워크로드를 먼저 statevector에서, 그 다음 runtime에서 실행합니다.

출력

job.result()는 일반 딕셔너리를 반환합니다:

{
"times": [...], # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)
"expectation_values": [[...]], # shape (n_times, n_observables)
"observable_labels": [...], # for example: ["Z_0", "ZZ_0_1"]
"metadata": {
"n", "t_steps", "dt", "tier",
"aqc_compressed_steps": 5, # total compressed steps (= sum of segment n_steps)
"aqc_segments": [ # per segment: the plan plus its own results
{"n_steps": 3, "ansatz_steps": 1, "steps": [1, 2, 3], "n_params": 133,
"fidelities": {"1": ..., "2": ..., "3": ...}},
{"n_steps": 2, "ansatz_steps": 2, "steps": [4, 5], "n_params": 245,
"fidelities": {"4": ..., "5": ...}},
],
"execution_backend",
"aqc_fidelities": {"1": ..., "2": ...}, # flat per-step fidelity, all compressed steps
"circuit_stats": { # per-step 2q depth and gate count, full Trotter vs AQC
"1": {"full_trotter": {"depth_2q": ..., "num_2q_gates": ...},
"aqc_trotter": {"depth_2q": ..., "num_2q_gates": ...}},
"2": {...},
},
"warnings": [...], # non-fatal notices; for example, a cotengrust fallback
"resource_usage": { # per stage; QPU_TIME is the charged QPU time
"RUNNING: OPTIMIZING_FOR_HARDWARE": {"CPU_TIME": ...},
"RUNNING: WAITING_FOR_QPU": {"CPU_TIME": ...},
"RUNNING: EXECUTING_QPU": {"QPU_TIME": ...},
},
},
# present only when return_circuits=True
"circuits": [QuantumCircuit, ...], # one per evolved step; circuits[i] is at times[i + 1]
}

aqc_fidelitiescircuit_stats는 가장 먼저 읽어야 할 두 가지입니다: 이 둘은 압축이 충실했는지, 그리고 실제로 깊이를 절약했는지를 알려줍니다. runtime에서 resource_usage는 청구되는 QPU 시간과 별도로 대기열 대기 시간을 보고합니다. 거부된 입력은 구조화된 ServerlessError(코드 4615)로 즉시 실패합니다.

시뮬레이터 예제

먼저 정확한 statevector Backend에서 함수를 실행합니다. QPU 시간을 사용하지 않으며 배포를 처음부터 끝까지 검증합니다. 여기 모델은 8-Qubit 횡자기장 이징 체인이며, observables는 생략되어 함수가 기본 사이트별 ZZ를 측정합니다.

압축 계획은 이해할 가치가 있는 입력입니다. 각 세그먼트 {"n_steps": k, "ansatz_steps": m}k개의 연속된 Trotter 스텝을 m-스텝 Trotter 타겟으로 구축된 안자츠로 압축하며, sum(n_steps)을 넘어서는 스텝은 일반 Trotter로 실행됩니다. 초기의 얽힘이 낮은 스텝은 얕은 단일 레이어 안자츠로 잘 압축되며, 이후 더 얽힌 스텝은 더 깊은 안자츠가 필요합니다.

from qiskit.quantum_info import SparsePauliOp

fn = serverless.load("aqc-dynamics-function")

n = 8
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)

job = fn.run(
t_steps=8,
aqc_segments=[
{
"n_steps": 4,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 2,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="statevector",
)
print("job ID:", job.job_id)
job ID: ee1f3793-e995-427d-81d1-5924549beb38

실행 추적 및 결과 읽기

status()는 개략적인 작업 생명 주기와 함수가 실행하면서 게시하는 단계별 하위 상태를 모두 보고합니다. 이 가이드의 뒷부분에 나오는 하드웨어 실행에도 동일한 단계가 적용됩니다:

QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE

status()단계
RUNNING: OPTIMIZING_FOR_HARDWARE상태 준비, Trotter 구축, AQC 압축
RUNNING: WAITING_FOR_QPUQPU에서 대기 중(runtime Backend에서만)
RUNNING: EXECUTING_QPUCircuit 실행 중(로컬 시뮬레이터는 이를 직접 표시함)
RUNNING: POST_PROCESSING결과 딕셔너리 조립 중

터미널 상태는 DONE, ERROR, CANCELED입니다. 이 statevector 실행은 QPU 대기열이 없으므로 RUNNING: WAITING_FOR_QPU를 건너뜁니다. 언제든지 job.logs()를 사용하여 각 단계에서 도달한 AQC 충실도를 포함한 단계별 로그를 확인할 수 있습니다.

print(job.status()) # re-run until this reports DONE
DONE
import numpy as np

result = job.result()
ev = np.array(result["expectation_values"])

print("observables:", result["observable_labels"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("first row (t = 0, the prepared state):", np.round(ev[0], 4))
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']
shape: (9, 8) -> (n_times, n_observables)
first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]
last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}
2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)

하드웨어 예제

backend="runtime"으로 함수를 호출하면 실제 IBM Quantum 프로세서에서 트랜스파일 및 실행되며, 함수에 내장된 오류 완화 기법인 동적 디커플링(XY4), Gate 트월링, 트월드 판독 오류 소거(TREX)가 적용됩니다. backend_name은 디바이스를 선택하며, 생략하면 함수가 가장 한가한 디바이스를 선택합니다.

과학 코드는 전혀 바뀌지 않습니다. 시뮬레이터 예제와 다른 점은 체인 길이, Trotter 스텝 수, 압축 계획, Backend, 그리고 다음 섹션에서 다루는 명시적인 완화 설정입니다.

제어 하드웨어에 맞게 작업 규모 조정하기

estimator_options는 신중하게 설정할 가치가 있는 입력입니다. Gate 트월링은 모든 PUB에 대해 num_randomizations개의 별도 랜덤화된 Circuit을 구축하며, 전체 작업(모든 랜덤화를 포함한 모든 PUB)은 QPU의 클래식 제어 시스템의 명령 메모리에 맞아야 합니다. 함수는 기본적으로 1000개의 랜덤화를 사용하므로, 10-스텝 진화는 각각 1000개의 Circuit으로 이루어진 11개의 PUB, 즉 단일 작업에서 약 11,000개의 Circuit 인스턴스를 제출합니다.

제어 시스템이 보유할 수 있는 용량을 초과하면 작업은 오류 6073으로 실패합니다. 작업 제한은 그 임계값과 이를 계산하는 방법을 제공하며, 주요 임계값은 PUB당이 아니라 작업당으로 적용되는 Qubit당 2,680만 개의 제어 시스템 명령입니다. 동적 디커플링은 여기에 포함되는 Gate를 추가합니다.

두 가지 입력이 크기를 제어합니다:

  • estimator_options는 shot 예산을 설정합니다. 총 shot 수는 num_randomizations * shots_per_randomization이므로, 통계를 유지하면서도 무작위화 횟수와 무작위화당 shot 수를 서로 조절하여 프로그램을 줄일 수 있습니다. 다음 셀은 무작위화 100회에 각 200 shot을 사용하며, 이는 observable당 20,000 shot으로, 기본값이 제출할 Circuit 인스턴스의 약 10분의 1에 해당합니다. 전체 필드 목록은 TwirlingOptionsEstimator options를 참조하세요.

  • batches는 PUB를 그만큼의 개별 런타임 작업으로 분할하며, 이는 오류 6073 자체가 제안하는 해결책이자 작업 단위 구성이 중요한 이유입니다. batches=4를 설정하면 11개를 한 번에 보내는 대신 작업당 약 3개의 PUB를 전송하며, 작업들이 하나의 배치로 함께 나가므로 그룹이 각 작업을 개별적으로 큐잉하는 대신 한 번만 큐잉됩니다.

제공된 estimator_options는 함수의 기본값에 병합되는 것이 아니라 통째로 대체한다는 점을 기억하세요. 따라서 동적 디커플링과 TREX를 계속 켜두려면 다음 셀에서 다시 명시해야 합니다.

from qiskit.quantum_info import SparsePauliOp

fn = serverless.load("aqc-dynamics-function")

n = 10
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)

job = fn.run(
t_steps=10,
aqc_segments=[
{
"n_steps": 3,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 3,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="runtime",
backend_name="ibm_marrakesh",
# The function defaults to 1000 twirling randomizations, which was too large
# for this device. Total shots is num_randomizations *
# shots_per_randomization, so this is 20,000 shots per observable.
estimator_options={
"dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
"twirling": {
"enable_gates": True,
"num_randomizations": 100,
"shots_per_randomization": 200,
},
"resilience": {"measure_mitigation": True},
},
)
print("job ID (save this to reconnect later):", job.job_id)
job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9
장시간 실행되는 작업에 다시 연결하기

하드웨어 실행은 빠르지 않으며, 대부분의 시간은 QPU가 아닌 고전적인 처리에 소요됩니다. AQC 압축은 QPU에 도달하기 전에 함수 내부에서 실행되며, QPU 대기열은 그 위에 추가됩니다. 실행되는 동안 이 노트북이나 커널을 계속 열어 둘 필요는 없습니다.

이전 셀에서 출력된 작업 ID를 복사하여 저장하세요. 다음 세 개의 셀을 통해 나중에 다시 이어서 진행할 수 있습니다:

  1. 다시 연결: 새 커널 세션에서만 필요합니다. Authentication 셀을 다시 실행하여 serverless를 재생성한 다음, 저장한 ID로 job 핸들을 재구성하세요. 제출한 세션에 계속 있는 경우 핸들이 이미 활성 상태이므로 이 셀은 건너뛰세요.

  2. 상태 확인: DONE으로 보고될 때까지 다시 실행하세요.

  3. 결과 가져오기: 상태가 DONE일 때만 실행하세요.

저장한 ID를 다음 재연결 셀의 자리 표시자 위에 붙여넣으세요.

# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Re-run this until it reports DONE, then fetch the result in the following cell.
print(job.status())
DONE
import numpy as np

# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits.
result = job.result()
ev = np.array(result["expectation_values"])

print("backend:", result["metadata"]["execution_backend"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
backend: runtime
shape: (11, 10) -> (n_times, n_observables)
last row (t = t_steps * dt): [0.1504 0.1361 0.218 0.2144 0.2275 0.1783 0.1749 0.1599 0.0915 0.0922]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}
2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)

다음 단계

권장 사항