Qiskit AI 기반 트랜스파일러 소개
예상 소요 시간: IBM Heron에서 5분 (참고: 이는 단지 예상치일 뿐입니다.) (실행 시간은 다를 수 있습니다.)
학습 성과
- 표준 트랜스파일러를 그대로 대체할 수 있는 AI 기반 트랜스파일러(
generate_ai_pass_manager) 사용 방법 - AI 기반 트랜스파일러와 기본 트랜스파일러를 2-큐비트 깊이, 게이트 수, 트랜스파일링 시간 측면에서 비교한 결과
- 미러 회로를 활용하여 하드웨어 실행을 통해 트랜스파일링 품질을 평가하는 방법
전제조건
배경
Qiskit의 AI 기반 트랜스파일러는 SABRE와 같은 기존의 휴리스틱 방식보다 더 짧고 하드웨어 효율이 높은 회로를 생성할 수 있는 머신러닝 기반 트랜스파일링 단계를 도입했습니다. 회로 길이가 짧을수록 노이즈가 덜 축적되므로, 실제 양자 하드웨어에서 결과의 품질이 직접적으로 향상됩니다.
이 튜토리얼에서는 두 가지 트랜스파일링 전략을 비교해 보겠습니다:
전략 | API |
|---|---|
| 기본값 | generate_preset_pass_manager(optimization_level=3, ...) |
| 인공지능 | generate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...) |
각 전략에 대해 세 가지 지표, 즉 2-큐비트 게이트 깊이, 총 게이트 수, 트랜스필레이션 실행 시간을 측정합니다.
AI 기반 트랜스파일러 벤치마크
벤치마크 테스트 결과, AI 기반 트랜스파일러는 표준 Qiskit 트랜스파일러에 비해 일관되게 더 얕고 품질이 높은 회로를 생성해 냈다. generate_preset_pass_manager이 테스트에서는.로 구성된 Qiskit의 기본 패스 매니저 전략을 사용했습니다. 이 기본 전략은 대개 효과적이지만, 규모가 크거나 더 복잡한 회로에서는 제대로 작동하지 않을 수 있습니다. 반면, AI 기반 패스는 IBM Quantum® 하드웨어의 ‘헤비-헥스(heavy-hex)’ 토폴로지로 트랜스파일링할 때, 대규모 회로(100 큐비트 이상)의 경우 2-큐비트 게이트 수를 평균 24% 줄이고 회로 깊이를 36% 감소시키는 성과를 거두었다. 이 벤치마크에 대한 자세한 내용은 이 블로그 를 참조하십시오.
이 튜토리얼에서는 AI 패스의 주요 장점과 기존 방식과의 비교점을 살펴봅니다.
요구사항
이 튜토리얼을 시작하기 전에 다음 항목이 설치되어 있는지 확인하십시오:
- Qiskit SDK v2.0 또는 그 이후 버전이며, 시각화 기능을 지원합니다
- Qiskit Runtime
pip install qiskit-ibm-runtimev0.22 이상 - AI 로컬 모드를 지원하는 Qiskit IBM 트랜스파일러 (
pip install 'qiskit-ibm-transpiler[ai-local-mode]') - Qiskit Aer (
pip install qiskit-aer)
설정
from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_transpiler import generate_ai_pass_manager
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error
import matplotlib.pyplot as plt
from statistics import mean, stdev
import time
import logging
seed = 42
def transpile_with_metrics(pass_manager, circuit):
"""Transpile a circuit and return the result along with key metrics."""
start = time.time()
qc_out = pass_manager.run(circuit)
elapsed = time.time() - start
depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)
gate_count = qc_out.size()
return qc_out, {
"depth_2q": depth_2q,
"gate_count": gate_count,
"time_s": round(elapsed, 3),
}
def remap_to_contiguous(tqc):
"""Remap a transpiled circuit to use contiguous qubit indices.
Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)
on a large backend. This remaps them to 0, 1, 2, ... so Aer only
simulates the active qubits."""
active = sorted(
{tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}
)
qubit_map = {old: new for new, old in enumerate(active)}
new_qc = QuantumCircuit(len(active))
for inst in tqc.data:
old_indices = [tqc.find_bit(q).index for q in inst.qubits]
new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])
return new_qc
def build_mirror_circuit(tqc, simulate=True):
"""Build a mirror circuit: U followed by U-dagger, with measurements.
The expected output is always |0...0>, so measuring the survival
probability reveals how much noise each transpilation strategy adds.
Args:
tqc: A transpiled circuit.
simulate: If True (default), remap to contiguous qubits so Aer
only simulates the active qubits. If False, keep the full
physical layout for hardware execution."""
if simulate:
tqc = remap_to_contiguous(tqc)
mirror = tqc.compose(tqc.inverse())
mirror.measure_all()
return mirror
def print_summary(results):
"""Print a summary of each metric as mean +/- stdev across all circuits,
along with the mean percentage improvement of AI over Default."""
metrics = [
("Depth 2Q", "Depth 2Q (Default)", "Depth 2Q (AI)"),
("Gate Count", "Gate Count (Default)", "Gate Count (AI)"),
("Time (s)", "Time (Default)", "Time (AI)"),
]
header = (
f"{'Metric':<12}{'Default (mean +/- std)':>24}"
f"{'AI (mean +/- std)':>22}{'AI % improvement':>22}"
)
print(header)
print("-" * len(header))
for label, col_def, col_ai in metrics:
defaults = [r[col_def] for r in results]
ais = [r[col_ai] for r in results]
pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]
default_str = f"{mean(defaults):.1f} +/- {stdev(defaults):.1f}"
ai_str = f"{mean(ais):.1f} +/- {stdev(ais):.1f}"
pct_str = f"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%"
print(f"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}")
def plot_metrics_and_pct(results, title_prefix):
"""Plot metric comparisons and percentage improvement of AI over Default."""
qubits = [r["Qubits"] for r in results]
metrics = [
("Depth 2Q (Default)", "Depth 2Q (AI)", "Two-Qubit Depth"),
("Gate Count (Default)", "Gate Count (AI)", "Gate Count"),
("Time (Default)", "Time (AI)", "Transpilation Time"),
]
# Row 1: raw metric comparison
fig, axs = plt.subplots(1, 3, figsize=(21, 5))
fig.suptitle(
f"{title_prefix}: Metric Comparison",
fontsize=15,
fontweight="bold",
y=1.02,
)
for ax, (col_def, col_ai, label) in zip(axs, metrics):
ax.plot(qubits, [r[col_def] for r in results], "o-", label="Default")
ax.plot(qubits, [r[col_ai] for r in results], "s-", label="AI")
ax.set_title(label)
ax.set_xlabel("Number of Qubits")
ax.set_ylabel(label)
ax.legend()
plt.tight_layout()
plt.show()
# Row 2: percentage improvement
fig, axs = plt.subplots(1, 3, figsize=(21, 5))
fig.suptitle(
f"{title_prefix}: % Improvement of AI over Default",
fontsize=15,
fontweight="bold",
y=1.02,
)
for ax, (col_def, col_ai, label) in zip(axs, metrics):
pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]
ax.axhline(
0, color="#1f77b4", linewidth=2, label="Default (baseline)"
)
ax.plot(qubits, pct, "s-", color="#ff7f0e", label="AI")
ax.fill_between(qubits, 0, pct, alpha=0.15, color="#ff7f0e")
ax.set_title(label)
ax.set_xlabel("Number of Qubits")
ax.set_ylabel("% Improvement")
ax.legend()
plt.tight_layout()
plt.show()
# Suppress verbose AI-powered transpiler logs
logging.getLogger(
"qiskit_ibm_transpiler.wrappers.ai_local_synthesis"
).setLevel(logging.WARNING)소규모 시뮬레이터 예시
1단계: 고전적 입력을 양자 문제에 매핑하기
우리는 큐비트 수가 6개에서 25개 사이인, 깊이가 4인 임의의 회로 20개를 생성합니다. 이 회로들은 트랜스파일링 전략을 비교하기 위한 테스트 케이스로 사용될 것입니다.
num_circuits_sim = 20
depth_sim = 4
qubit_range_sim = list(range(6, 26))
circuits_sim = [
# We have only two qubit gates, as those test how well the
# transpiler can optimize the circuit.
random_circuit(
num_qubits=n,
depth=depth_sim,
max_operands=2,
num_operand_distribution={2: 1},
seed=seed + i,
)
for i, n in enumerate(qubit_range_sim)
]
print(
f"Created {len(circuits_sim)} circuits with qubit counts "
f"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}"
)
circuits_sim[0].draw(output="mpl", fold=-1)Output:
Created 20 circuits with qubit counts from 6 to 25
2단계: 양자 하드웨어 실행을 위한 문제 최적화
선택한 백엔드에 대한 기본(SABRE) 패스 관리자를 생성합니다. 두 변환 전략 모두 백엔드의 전체 결합 맵을 대상으로 합니다. 시뮬레이션 단계에서 각 트랜스파일된 회로를 활성 큐비트만으로 재표기하기 remap_to_contiguous 때문에, 이후의 국소 시뮬레이션은 처리 가능한 수준을 유지하게 되며, 이에 따라 Aer는 전체 디바이스 대신 해당 큐비트들만 시뮬레이션하게 됩니다.
service = QiskitRuntimeService()
backend = service.least_busy(
min_num_qubits=100, operational=True, simulator=False
)
pm_default_sim = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
seed_transpiler=seed,
)results_sim = []
for i, qc in enumerate(circuits_sim):
n = qubit_range_sim[i]
qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)
# Create a fresh AI pass manager each iteration to avoid stale layout state
pm_ai = generate_ai_pass_manager(
optimization_level=1,
ai_optimization_level=3,
backend=backend,
)
qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)
results_sim.append(
{
"Qubits": n,
"Depth 2Q (Default)": m_default["depth_2q"],
"Depth 2Q (AI)": m_ai["depth_2q"],
"Gate Count (Default)": m_default["gate_count"],
"Gate Count (AI)": m_ai["gate_count"],
"Time (Default)": m_default["time_s"],
"Time (AI)": m_ai["time_s"],
}
)
print_summary(results_sim)Output:
Fetching 4 files: 0%| | 0/4 [00:00<?, ?it/s]
Metric Default (mean +/- std) AI (mean +/- std) AI % improvement
--------------------------------------------------------------------------------
Depth 2Q 33.0 +/- 12.9 26.4 +/- 8.0 +15.8% +/- 17.6%
Gate Count 522.0 +/- 266.0 560.5 +/- 279.1 -9.0% +/- 9.0%
Time (s) 0.0 +/- 0.0 0.2 +/- 0.1 -893.6% +/- 362.9%
이 요약 표에는 전체 20개 회로에 대한 각 지표의 평균과 표준편차, 그리고 기본 설정 대비 AI 기반 트랜스파일러의 평균 개선율이 백분율로 표시되어 있습니다. 양수 값은 AI 기반 트랜스파일러가 더 나은 결과를 산출했음을 나타내며, 음수 값은 기본 설정이 더 나았음을 나타냅니다.
이 소규모 예시의 경우, AI 기반 트랜스파일러는 평균적으로 2-큐비트 깊이를 약 16% 줄이는 성과를 보였으나, 그 대가로 게이트 수가 약 9% 증가했습니다. 이는 두 전략 중 하나를 선택할 때 고려해야 할 핵심적인 상충 관계를 잘 보여줍니다. 즉, AI 기반 트랜스파일러는 깊이 감소(2-큐비트 게이트의 연속적인 레이어 수 감소)를 우선시하는 반면, 기본 트랜스파일러(SABRE)는 총 게이트 수 최소화(SWAP 삽입 횟수 감소)를 우선시합니다. 사용 목적에 따라 한 지표가 다른 지표보다 더 중요할 수 있습니다.
plot_metrics_and_pct(results_sim, "Small-Scale Random Circuits")Output:
2-큐비트 깊이: AI 기반 트랜스파일러는 일반적으로 2-큐비트 깊이가 더 낮은 회로를 생성합니다. 깊이는 AI 라우팅 모델이 최적화하도록 훈련된 주요 지표 중 하나이며, 대부분의 회로 규모에서 그 개선 효과가 뚜렷하게 나타나지만, 개별 회로에서는 SABRE가 이를 따라잡거나 능가하기도 합니다.
게이트 수: 이 규모에서는 양측의 결과가 박빙을 이루고 있으며, 전반적으로 SABRE가 근소한 우위를 점하고 있습니다. SABRE의 라우팅 휴리스틱은 삽입되는 SWAP 게이트의 수를 최소화하도록 설계되어 있어, 이를 통해 게이트 수를 직접적으로 줄일 수 있습니다. 회로 크기가 작을 때는 그 차이가 미미합니다.
트랜스파일레이션 시간: SABRE의 실행 시간은 큐비트 수와 관계없이 거의 일정하므로, 이 규모에서는 회로 크기가 트랜스파일레이션 시간에 미치는 영향이 미미합니다. SABRE의 핵심 라우팅 로직은 고도로 최적화되어 있습니다(대부분 Rust로 구현됨). AI 기반 트랜스파일러는 처리 시간이 눈에 띄게 더 오래 걸리며 회로 크기에 따라 처리 시간이 증가하지만, 절대적인 처리 시간은 대화형 사용에 있어서는 여전히 합리적인 수준입니다.
3단계: Qiskit primitives 명령어로 실행합니다
트랜스필레이션이 회로 정확도에 미치는 영향을 평가하기 위해, 10-큐비트 사례에 기반한 미러 회로를 구축하고, 간단한 노이즈 모델을 적용하여 Aer 시뮬레이터에서 실행해 보십시오. 미러 회로의 예상 출력은 항상 모두 0으로 구성된 비트열이므로, 가 측정될 확률은 각 트랜스필레이션 전략이 충실도를 얼마나 잘 보존하는지를 보여줍니다.
# Use the 10-qubit circuit (index where qubits == 10)
idx_10q = qubit_range_sim.index(10)
qc_10q = circuits_sim[idx_10q]
qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)
pm_ai = generate_ai_pass_manager(
optimization_level=1,
ai_optimization_level=3,
backend=backend,
)
qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)
tqc_methods = {
"Default": qc_default_10q,
"AI": qc_ai_10q,
}
print(
f"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}"
)
print(f"AI: depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}")Output:
Default: depth 84, gates 280
AI: depth 91, gates 343
# Build a simple depolarizing noise model
noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(
depolarizing_error(0.001, 1),
["sx", "x", "rz"], # ~0.1% per 1Q gate
)
noise_model.add_all_qubit_quantum_error(
depolarizing_error(0.01, 2),
["cx", "ecr"], # ~1% per 2Q gate
)
aer_sim = AerSimulator(noise_model=noise_model)
shots = 10000
survival_probs = {}
for method, tqc in tqc_methods.items():
mirror = build_mirror_circuit(tqc, simulate=True)
sampler = SamplerV2(mode=aer_sim)
job = sampler.run([mirror], shots=shots)
counts = job.result()[0].data.meas.get_counts()
all_zeros = "0" * mirror.num_qubits
survival = counts.get(all_zeros, 0) / shots
survival_probs[method] = survival
print(
f"{method:8s} P(|00...0>) = {survival:.4f} "
f"({counts.get(all_zeros, 0)}/{shots})"
)Output:
Default P(|00...0>) = 0.8460 (8460/10000)
AI P(|00...0>) = 0.8121 (8121/10000)
우리는 간단한 탈분극 잡음 모델을 적용하여 두 미러 회로를 모두 Aer 시뮬레이터에서 실행했습니다. ‘생존 확률’은 모든 비트가 0인 비트열을 반환하는 샷의 비율로 정의되며, 각 트랜스필레이션 전략이 얼마나 많은 노이즈를 유발하는지를 정량화합니다.
4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환
두 실행 결과에서 모두 0으로만 구성된 비트열이 관측될 확률을 구합니다. 생존 확률이 높을수록 충실도가 더 높다는 것을 의미하며, 이는 변환 과정에서 발생한 노이즈가 적었음을 뜻합니다. 아래 그래프는 보충값인 1 - P(|0...0>)를 나타내며, 하단 막대가 낮을수록 충실도가 높음을 의미하므로 오차의 미세한 차이도 더 쉽게 확인할 수 있습니다.
# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs = {method: 1 - p for method, p in survival_probs.items()}
fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
error_probs.keys(),
error_probs.values(),
color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title("Mirror Circuit Error (10-qubit, Aer Simulator)")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()Output:
이 경우, 기본 트랜스파일러는 이 특정 10-큐비트 인스턴스에 대해 더 얕고 크기도 더 작은 회로를 생성했기 때문에, 더 높은 충실도를 보일 것으로 예상됩니다. 회로별 결과는 제각각입니다. 위의 요약 표에서 볼 수 있듯이, AI 기반 트랜스파일러의 장점은 모든 개별 회로에서 나타나는 것이 아니라, 평균적으로 더 낮은 2-큐비트 깊이에서 나타납니다. 어떤 전략이 더 높은 충실도를 제공하는지는 각 측정 지표 간의 차이 크기, 하드웨어의 노이즈 특성, 그리고 회로 구조에 따라 달라집니다. 균일한 탈분극 잡음 모델 하에서는, 게이트 총 수가 깊이 자체보다 누적 오류에 더 직접적인 영향을 미치는 경우가 많습니다.
대규모 하드웨어 예시
1~4단계
여기서는 이러한 모든 세부 사항들을 더 큰 규모에서 명확한 워크플로로 통합한 뒤, 이를 실제 양자 하드웨어에서 실행합니다.
아래 코드는 퀀텀 비트 수가 26에서 50 사이인, 깊이가 8인 임의의 회로 25개를 생성합니다. 그런 다음 이 회로들은 두 가지 전략 모두를 적용하여 트랜스파일링되며, 동일한 메트릭이 수집됩니다. 그런 다음 26-큐비트 사례에 기반하여 미러 회로를 구축하고, 이를 실제 백엔드에 제출합니다.
# -------------------------Step 1-------------------------
num_circuits_hw = 25
depth_hw = 8
qubit_range_hw = list(range(26, 51))
circuits_hw = [
# We have only two qubit gates, as those test how well the
# transpiler can optimize the circuit.
random_circuit(
num_qubits=n,
depth=depth_hw,
max_operands=2,
num_operand_distribution={2: 1},
seed=seed + i,
)
for i, n in enumerate(qubit_range_hw)
]
print(
f"Created {len(circuits_hw)} circuits with qubit counts "
f"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}"
)Output:
Created 25 circuits with qubit counts from 26 to 50
# -------------------------Step 2-------------------------
pm_default = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
seed_transpiler=seed,
)
results_hw = []
for i, qc in enumerate(circuits_hw):
n = qubit_range_hw[i]
qc_default, m_default = transpile_with_metrics(pm_default, qc)
# Create a fresh AI pass manager each iteration to avoid stale layout state
pm_ai = generate_ai_pass_manager(
optimization_level=1,
ai_optimization_level=3,
backend=backend,
)
qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)
results_hw.append(
{
"Qubits": n,
"Depth 2Q (Default)": m_default["depth_2q"],
"Depth 2Q (AI)": m_ai["depth_2q"],
"Gate Count (Default)": m_default["gate_count"],
"Gate Count (AI)": m_ai["gate_count"],
"Time (Default)": m_default["time_s"],
"Time (AI)": m_ai["time_s"],
}
)
print_summary(results_hw)Output:
Metric Default (mean +/- std) AI (mean +/- std) AI % improvement
--------------------------------------------------------------------------------
Depth 2Q 217.4 +/- 50.4 191.0 +/- 35.6 +10.9% +/- 10.7%
Gate Count 4513.3 +/- 1394.3 5227.1 +/- 1536.4 -16.4% +/- 5.8%
Time (s) 0.1 +/- 0.0 3.5 +/- 1.5 -3588.2% +/- 643.6%
plot_metrics_and_pct(results_hw, "Large-Scale Random Circuits")Output:
# -------------------------Step 3-------------------------
# Build mirror circuits from the 26-qubit case
idx_26q = qubit_range_hw.index(26)
qc_26q = circuits_hw[idx_26q]
qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)
pm_ai = generate_ai_pass_manager(
optimization_level=1,
ai_optimization_level=3,
backend=backend,
)
qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)
mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)
mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)
# Re-transpile to basis gates (the inverse can introduce gates like sxdg)
pm_basis = generate_preset_pass_manager(
optimization_level=0,
backend=backend,
)
mirror_default_hw = pm_basis.run(mirror_default_hw)
mirror_ai_hw = pm_basis.run(mirror_ai_hw)
print(
f"Mirror circuit (Default): depth {mirror_default_hw.depth()}, "
f"gates {mirror_default_hw.size()}"
)
print(
f"Mirror circuit (AI): depth {mirror_ai_hw.depth()}, "
f"gates {mirror_ai_hw.size()}"
)
# Submit to real hardware
sampler_hw = SamplerV2(mode=backend)
sampler_hw.options.environment.job_tags = ["TUT_AITI"]
shots_hw = 500000
job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)
print(f"Job submitted: {job_hw.job_id()}")Output:
Mirror circuit (Default): depth 1577, gates 9672
Mirror circuit (AI): depth 1235, gates 11092
Job submitted: d8gt7vm6983c73dqbg0g
# -------------------------Step 4-------------------------
result_hw = job_hw.result()
survival_probs_hw = {}
for i, method in enumerate(["Default", "AI"]):
counts = result_hw[i].data.meas.get_counts()
mirror = [mirror_default_hw, mirror_ai_hw][i]
all_zeros = "0" * mirror.num_qubits
survival = counts.get(all_zeros, 0) / shots_hw
survival_probs_hw[method] = survival
print(
f"{method:8s} P(|00...0>) = {survival:.4f} "
f"({counts.get(all_zeros, 0)}/{shots_hw})"
)
# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}
fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
error_probs_hw.keys(),
error_probs_hw.values(),
color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title(f"Mirror Circuit Error (26-qubit, {backend.name})")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()Output:
Default P(|00...0>) = 0.0005 (239/500000)
AI P(|00...0>) = 0.0050 (2516/500000)
결과 분석
이 대규모 분석 결과는 소규모 사례에서 관찰된 경향을, 이제 더 까다로운 규모에서 다시 한 번 뒷받침해 주고 있다.
2-큐비트 깊이: AI 기반 트랜스파일러는 모든 회로 크기 범위에서 2-큐비트 깊이를 눈에 띄게 낮추는 성과를 지속적으로 보여주고 있습니다. 깊이 최적화는 AI 라우팅 모델이 학습되는 주요 목표 중 하나이며, 큐비트 수가 많아질수록 휴리스틱 기법으로 라우팅 문제를 해결하기가 더 어려워지기 때문에 이 모델의 장점이 더욱 두드러집니다.
게이트 수: 기본 트랜스파일러(SABRE)는 이 범위 내의 모든 회로 규모에서 일관되게 게이트 수가 더 적은 회로를 생성합니다. SABRE의 휴리스틱은 게이트 수를 최소화하도록 특별히 설계되었으며, 이 규모에서는 그 장점이 명확하고 일관되게 나타납니다.
전사 시간: 규모가 커질수록 전사 시간의 격차가 더 벌어진다. SABRE는 거의 일정한 수준을 유지하는 반면, AI 기반 트랜스파일러의 실행 시간은 더 가파르게 증가합니다. 그럼에도 불구하고, AI 기반 트랜스파일러 런타임은 대부분의 워크플로우에서 여전히 실용적입니다.
미러 회로의 정확도: 두 방법 모두 이 규모에서는 생존 확률이 1%를 훨씬 밑도는 결과를 보여, 활용 가능한 신호가 거의 남지 않습니다. 총 게이트 수가 약 10,000개에 달하고 2-큐비트 연산 깊이가 1,000을 초과함에 따라, 미러 회로 전반에 걸쳐 누적된 탈분극 잡음이 신호의 대부분을 압도한다. 이는 미러 회로 방식의 주요 한계를 잘 보여준다. 이 방식은 간단하고 고전적 시뮬레이션이 필요하지 않지만, 대규모 또는 심층 회로에서는 확장성이 떨어지는데, 이러한 회로에서는 두 방법 모두 노이즈 플로어에 근접하게 되며, 남아 있는 미약한 신호는 누적된 오차에 의해 지배되기 때문이다.
이러한 결과는 AI 기반 트랜스파일러의 효과성을 입증해 주지만, 그 한계점도 유념해야 한다. 현재 AI 합성 방식은 특정 결합 맵에서만 사용할 수 있어, 그 적용 범위가 제한될 수 있습니다. 다양한 시나리오에서 이 제약 조건의 적용 여부를 평가할 때는 이를 고려해야 합니다.
다음 단계
이 글이 흥미로웠다면, 다음 자료도 참고해 보시기 바랍니다: