TEM 함수를 사용하여 차동 이징 모델을 시뮬레이션합니다
알고리즘의 텐서 네트워크 오류 완화(TEM) 방법은 양자-고전 하이브리드 알고리즘으로, 노이즈 완화를 전적으로 고전적 후처리 단계 내에서 수행하도록 설계되었습니다. TEM을 통해 사용자는 관측 가능한 양자의 기대값을 계산할 수 있으며, 이는 양자 하드웨어에서 발생하는 불가피한 잡음 유발 오류를 정확도와 비용 효율성을 높여 완화시켜 양자 연구자와 산업 실무자 모두에게 매우 매력적인 선택지로 자리매김하고 있습니다.
이 튜토리얼은 TEM이 양자 시스템의 역학에 대해 의미 있는 결과를 얻을 수 있는 방법을 보여줍니다. 이는 오류 완화 없이는 접근할 수 없으며, PEC 및 ZNE와 같은 다른 오류 완화 방법을 사용할 경우 훨씬 더 많은 양자 자원이 필요합니다.
사용량 추정치: 이 노트북은 Heron r3 장치에서 약 10 QPU 분을 사용합니다. 실행 시간은 선택한 장치에 따라 크게 달라질 수 있습니다. 섹션별 사용량 추정치는 아래에서 확인할 수 있습니다.
TEM 기능을 사용하여 오류 완화 다체 물리 실험을 실행하십시오
이 튜토리얼은 다음 참고 문헌을 기반으로 합니다: L. E. Fischer 외, Nat. 실제 (2026). 이 참고문헌은 최대 91 큐비트 규모의 양자 하드웨어에서 수행된 실제 시뮬레이션에 대해 논의합니다. 이 튜토리얼에서는 더 작은 회로 크기로 유사한 시뮬레이션을 재현합니다.
킥드 아이징 모델은 일반적인 아이징 모델에 대응한다:
가로차기가 가해지는 대상:
목표는 횡방향 킥 이징 해밀토니언 하에서 상태의 역학을 시뮬레이션하는 것이며, 그 시간 진화는 플로케 유니터리 로 구현될 수 있다. 진화시킬 초기 상태는 첫 번째 큐비트가 상태 에 있고, 나머지 큐비트들은 쌍을 이루어 벨 상태 로 설정된 상태이다.
우리가 관찰하고자 하는 양은 상관 함수이다. 이 참고 논문은 이 양을 양자 큐비트( )에 대한 양자 파울리 연산자( )로 어떻게 재표현할 수 있는지 논의한다. 일정 수의 물리적 시간 단계 가 지난 후, 우리는 파울리 연산자 의 값을 계산한다. 시스템의 매개변수에 따라, 이 관측가능량의 값은 정확히 계산 가능한 값과 같거나, 근사적 방법으로만 시뮬레이션될 수 있다. 구체적으로, 의 경우 와 동일하며, 이는 본 튜토리얼 결과 벤치마킹에 사용할 값입니다. 또한, 주어진 시간 단계 에서, 는 0이다. 이러한 값을 얻는 방법과, 이러한 매개변수 범위를 벗어난 근사적 고전적 시뮬레이션 결과와의 비교에 대한 자세한 내용은 L. E. Fischer 외, Nat. 실제 (2026).
TEM은 먼저 회로 내 2-큐비트 게이트의 각 고유 레이어에 대한 노이즈를 특성화하고, 판독 오류를 특성화함으로써 작동합니다. 그런 다음, 회로가 양자 기계에서 실행됩니다. 마지막으로, 텐서 네트워크 오류 완화 처리가 IBM Cloud® 의 클래식 리소스에서 수행되며, 완화된 값이 반환됩니다. 이 예시에서 회로는 특성화할 두 개의 고유한 레이어를 가지고 있습니다.
설정
필수 조건으로, 필요한 종속성들이 설치되어 있는지 확인하십시오.
%pip install numpy matplotlib qiskit qiskit-ibm-catalog qiskit-ibm-runtime pylatexenc qiskit_qasm3_importimport os
from matplotlib import pyplot as plt
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit.qasm3 import load
from qiskit_ibm_catalog import QiskitFunctionsCatalogTEM을 통한 오류 완화
여기서 우리는 앞서 설명한 킥드 아이징 모델을 구현하는 회로를 제시한다. 회로는 다음과 같이 준비된다. 먼저, 상태 준비 단계가 있습니다. 이 단계에서 첫 번째 큐비트는 상태 에 있으며, 나머지 큐비트들은 벨 쌍 상태에 있습니다. 이어서 단위 진화 를 구현하는 벽돌 구조가 적용됩니다. 물리적 시간 단계 수는 회로 레이어 수에 해당합니다.
다음 코드는 이 튜토리얼에 필요한 두 개의 QASM 파일을 다운로드합니다.
# Download required QASM files
import urllib
urllib.request.urlretrieve(
"https://ibm.box.com/shared/static/swy5jtq309b0xpzluzlmsmj908yphes8.qasm",
"ki_30q.qasm",
)
urllib.request.urlretrieve(
"https://ibm.box.com/shared/static/et3gkodonw6gsp2trs43lzaozrdtiu7s.qasm",
"ki_12q.qasm",
)12개의 큐비트와 6개의 시간 단계로 구성된 회로의 축소 버전을 시각화할 수 있습니다:
# Parameters of the kicked Ising model
h = 0.0
num_qubits = 12
t_steps = 6
# Load the circuit for the kicked Ising model
small_circuit = load("ki_12q.qasm")
# Draw the circuit
small_circuit.draw("mpl", scale=0.25, fold=-1)Output:
다음으로 관측 가능 객체( )를 생성합니다. 이는 Qiskit에서 사용하는 순서와 일치하는 단순한 파울리 문자열로 구성됩니다:
def xt_observable(n_qubits, t_steps):
pauli_str = "".join(["I" * t_steps, "X", "I" * (n_qubits - t_steps - 1)])
pauli_str = pauli_str[::-1] # Reverse the string to match qiskit order
return SparsePauliOp(data=pauli_str, coeffs=1.0)우리의 작은 12큐비트 예시에서 관측 가능량은 다음과 같습니다:
# Build the observable for the kicked Ising model
small_observable = xt_observable(n_qubits=12, t_steps=6)
print(small_observable)Output:
SparsePauliOp(['IIIIIXIIIIII'],
coeffs=[1.+0.j])
Qiskit Functions PUB을 입력 수집 수단으로 사용하십시오. 우리의 경우, 단일 회로와 관측 가능량을 우리의 관측 가능 계량( PUB )으로 고려해 보자:
# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(small_circuit, [small_observable])]다음으로, TEM 기능에 접근할 수 있게 됩니다. 먼저 IBM Cloud 에 필요한 인증을 설정하고 사용 가능한 장치 중에서 백엔드를 선택합니다. 토큰, 사용 가능한 백엔드 및 해당 클라우드 리소스 이름(CRN) 은 IBM Quantum Platform 대시보드에서 계정에 로그인하여 확인할 수 있습니다.
# Set IBM Quantum credentials and backend configuration
personal_token = os.environ.get(
"QISKIT_IBM_TOKEN", "<API-KEY>"
) # Replace with your personal token or set the environment variable
channel = "ibm_quantum_platform"
crn = "your_crn" # Replace with the Cloud Resource Name (CRN)
# Select the QPU backend
backend_name = "ibm_qpu_name" # Replace with your desired backend's nameQiskit Functions Catalog 에서 TEM 함수를 불러옵니다:
# Load the TEM function from the Qiskit Functions Catalog
catalog = QiskitFunctionsCatalog(
channel=channel,
token=personal_token,
instance=crn,
)
tem = catalog.load("algorithmiq/tem")이제 TEM이 제공하는 오류 완화 기능을 적용하여 킥된 아이징 회로에 대한 실험을 수행할 수 있습니다. 기본 설정으로 TEM을 실행하면 QPU에 따라 예상 QPU 실행 시간이 약 2.5 분 정도로 간단하게 실행할 수 있습니다:
tem_job = tem.run(pubs=pubs, backend_name=backend_name)기본 옵션으로 TEM 함수는 양자 컴퓨터에서 세 가지 작업을 실행합니다: 잡음 학습, 판독 완화, 회로 샘플링. 이들 각각이 사용하는 샷 수는 함수에 전달되는 옵션에서 변경할 수 있습니다. 기본적으로 이러한 매개변수는 완화된 기대값에서 0.05 의 정밀도를 달성하도록 설정됩니다.
작업 상태는 IBM Quantum Platform 대시보드 에서 확인하거나 다음 명령어로 확인할 수 있습니다:
print(tem_job.status())Output:
QUEUED
상태가 일 때 DONE, 원시 결과와 완화된 결과를 확인할 수 있습니다. 아래에 정의된 tem_evs 는 요청된 관측량들의 기대값이며, 이 경우 단일 관측량인 에 해당하며, 와 tem_std 는 각각 대응하는 표준편차이다.
# Get the results of the TEM job
tem_results = tem_job.result()[
0
] # Get the first and only result from the job
tem_evs = tem_results.data.evs[0]
tem_std = tem_results.data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")Output:
TEM Result: 1.031 ± 0.046
각 호출에 사용된 양자 런타임의 양은 IBM Quantum Platform 에서 확인하거나, Python 코드의 결과 메타데이터를 검사하여 확인할 수 있습니다.
# Get the TEM job runtime
tem_runtime = tem_job.result().metadata["resource_usage"][
"RUNNING: EXECUTING_QPU"
]["QPU_TIME"]
print(f"TEM Runtime: {tem_runtime} seconds")Output:
TEM Runtime: 155.0 seconds
TEM 매개변수 및 고급 옵션 사용자 지정
TEM 기능은 오류 완화 워크플로를 사용자 정의할 수 있는 여러 고급 옵션을 제공합니다. 이러한 옵션을 통해 정밀도, 샷 수, 잡음 학습 전략 및 기타 매개변수를 제어하여 실험 요구사항과 사용 가능한 양자 자원에 더 잘 맞출 수 있습니다.
일반적인 고급 옵션은 다음과 같습니다:
- **
precision**완화된 기대값에 대한 목표 정밀도를 지정하십시오. - **
default_shots**대신 측정 작업에서 사용되는 샷precision수를 지정할 수 있습니다. - **
tem_max_bond_dimension**텐서 네트워크에서 사용되는 최대 결합 차원. - **
tem_compression_cutoff**텐서 네트워크에 사용할 컷오프 값. - 노이즈 학습 옵션 : 노이즈 특성화 방식을 구성합니다. 예를 들어 반복 횟수나 특정 교정 회로 등을 설정할 수 있습니다.
- **
private**회로 및 실험 결과가 본인에게만 비공개로 유지되도록 하고, 작업 결과의 다중 다운로드를 비활성화하십시오.
지원되는 옵션의 전체 목록과 설명은 TEM 문서 또는 Qiskit Functions Catalog 를 참조하십시오. 이러한 매개변수를 조정하여 실행 시간, 리소스 사용량 및 결과 정확도 간의 균형을 맞출 수 있습니다.
TEM 함수를 실행할 때 다음 옵션을 사전(dictionary) 형태로 인수로 options 전달할 수 있습니다:
options = {
"default_shots": 10_000,
"tem_max_bond_dimension": 512,
"tem_compression_cutoff": 1e-16,
# This option helps optimizing the measurement
# stage since the observable is strongly biased
# toward the X operator for all the qubits.
"compute_shadows_bias_from_observable": True,
# set to True to keep experiment results private,
# recommended for confidential circuits
"private": False,
}노이즈 학습기를 위한 사용자 지정 옵션도 전달할 수 있습니다. 이 정의들은 에서 사용된 정의를 따릅니다: qiskit-ibm-runtimeNoiseLearnerOptions
nl_options = {
"num_randomizations": 32,
"max_layers_to_learn": 2,
"shots_per_randomization": 128,
"layer_pair_depths": [0, 1, 2, 4, 16, 32],
}
# add noise learning options to the overall options
options |= nl_options이 회로에 맞춰 조정된 사용자 지정 옵션으로 실험을 다시 실행하십시오. 예상 실행 시간은 약 4 QPU 분입니다.
tem_job_custom = tem.run(
pubs=pubs, backend_name=backend_name, options=options
)작업이 비공개로 설정되지 않은 경우, 나중에 결과를 복구할 수 있습니다. 이를 위해 여기에 출력된 작업 ID를 저장하고 사용하십시오 tem_job_custom = catalog.get_job_by_id("your-job-id").
job_id = tem_job_custom.job_id
print(f"Job ID: {job_id}")Output:
Job ID: 1ba10094-a541-457a-9287-dbd49306d12d
results_custom = tem_job_custom.result()
tem_evs = results_custom[0].data.evs[0]
tem_std = results_custom[0].data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")Output:
TEM Result: 0.956 ± 0.018
이제 실험에 대한 통찰력을 얻기 위해 결과와 메타데이터를 확인할 수 있습니다:
metadata_custom = results_custom[0].metadata
unmitigated_evs = metadata_custom["evs_non_mitigated"][0]
unmitigated_stds = metadata_custom["stds_non_mitigated"][0]
print(f"Unmitigated Result: {unmitigated_evs:.3f} ± {unmitigated_stds:.3f}")
# Exact result for the kicked Ising model from the reference paper
exact_evs = np.cos(2 * h) ** t_steps
print("Exact Result:", exact_evs)Output:
Unmitigated Result: 0.894 ± 0.015
Exact Result: 1.0
# Plot comparing the different expectation values
plt.bar(
["Unmitigated", "TEM"],
[unmitigated_evs, tem_evs],
yerr=[unmitigated_stds, tem_std],
color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()Output:
마지막으로, 사용자 정의 옵션이 QPU 및 클래식 런타임에 미치는 영향을 확인할 수 있습니다:
# Get the metadata of the TEM job
job_metadata = results_custom.metadata
# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
"QPU_TIME"
]
classical_runtime = (
job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
"CPU_TIME"
]
+ job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)
print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")Output:
QPU Runtime: 342.0 seconds
Classical Runtime: 107.632604 seconds
대형 회로에 적용 가능한 TEM
대형 회로는 원칙적으로 TEM 기능으로 실행될 수 있다. 그러나 TEM이 실행 시간이 매우 길 수 있는 IBM Cloud 러너에서 실행된다는 점을 고려할 때, 고전적 자원의 한계를 인지하는 것이 중요합니다. 매우 큰 회로의 경우, qiskit\ [email protected] 으로 TEM 지원팀에 문의하십시오.
여기서는 더 큰 유틸리티 규모인 30큐비트 회로로 예시를 실행하며, 정확도보다는 속도를 위해 TEM 매개변수를 최적화합니다.
# Kicked Ising model parameters
n_qubits = 30
t_steps = 15
h = 0.0
# Load the circuit for the kicked Ising model
circuit = load("ki_30q.qasm")
# Build the observable for the kicked Ising model
observable = xt_observable(n_qubits=n_qubits, t_steps=t_steps)
# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(circuit, [observable])]성능 중심의 옵션을 정의해 보겠습니다:
options = {
"num_randomizations": 32,
"max_layers_to_learn": 2,
"shots_per_randomization": 128,
"layer_pair_depths": [0, 1, 2, 4, 16, 32, 64],
"default_shots": 5_000,
"tem_max_bond_dimension": 128,
"tem_compression_cutoff": 1e-10,
"compute_shadows_bias_from_observable": True,
"private": False,
}마지막으로 실험을 실행하고 결과를 얻은 후 시각화합니다. 이 작업에는 약 3.5 QPU 분이 소요될 것입니다.
tem_job_large = tem.run(pubs=pubs, backend_name=backend_name, options=options)job_id = tem_job_large.job_id
print(f"Job ID: {job_id}")Output:
Job ID: 9f3f190f-f4b0-4dcb-bb83-5f71f37d0d77
results_large = tem_job_large.result()
tem_evs = results_large[0].data.evs[0]
tem_std = results_large[0].data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")
# Get the metadata of the TEM job
job_metadata = tem_job_large.result().metadata
# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
"QPU_TIME"
]
classical_runtime = (
job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
"CPU_TIME"
]
+ job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)
print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")Output:
TEM Result: 0.794 ± 0.026
QPU Runtime: 203.0 seconds
Classical Runtime: 251.71805499999996 seconds
# Plot comparing the different expectation values
metadata_large = results_large[0].metadata
unmitigated_evs = metadata_large["evs_non_mitigated"][0]
unmitigated_stds = metadata_large["stds_non_mitigated"][0]
exact_evs = np.cos(2 * h) ** t_steps
plt.bar(
["Unmitigated", "TEM"],
[unmitigated_evs, tem_evs],
yerr=[unmitigated_stds, tem_std],
color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()Output: