주 콘텐츠로 건너뛰기

Q-CTRL의 Qiskit Functions를 활용한 양자 위상 추정

사용 예상 시간: Heron r2 프로세서 기준 40초. (참고: 이는 예상치이며, 실제 실행 시간은 다를 수 있습니다.)

배경

양자 위상 추정(QPE)은 양자 컴퓨팅의 핵심 알고리즘으로, Shor 알고리즘, 양자 화학 기저 상태 에너지 추정, 고유값 문제 등 많은 중요한 응용의 기반이 됩니다. QPE는 유니터리 연산자의 고유 상태에 연관된 위상 φ\varphi를 추정하며, 이는 다음 관계식으로 표현됩니다.

Uφ=e2πiφφ,U \lvert \varphi \rangle = e^{2\pi i \varphi} \lvert \varphi \rangle,

그리고 mm개의 계수 Qubit을 사용하여 ϵ=O(1/2m)\epsilon = O(1/2^m)의 정밀도로 위상을 결정합니다 [1]. 이 Qubit들을 중첩 상태로 준비하고, UU의 제어된 거듭제곱을 적용한 뒤, 역 양자 푸리에 변환(QFT)으로 위상을 이진 인코딩된 측정 결과로 추출함으로써, QPE는 φ\varphi를 근사하는 이진 분수에 해당하는 비트 문자열에서 확률 분포가 집중됩니다. 이상적인 경우, 가장 높은 측정 확률을 가진 비트 문자열이 위상의 이진 전개에 직접 대응하며, 다른 결과의 확률은 계수 Qubit 수에 따라 빠르게 감소합니다. 그러나 하드웨어에서 깊은 QPE Circuit을 실행하면 어려움이 발생합니다. 많은 수의 Qubit과 얽힘 연산이 알고리즘을 결맞음 파괴 및 Gate 오류에 매우 민감하게 만들기 때문입니다. 이로 인해 비트 문자열 분포가 넓어지고 이동하여 실제 고유 위상이 가려집니다. 결과적으로, 확률이 가장 높은 비트 문자열이 더 이상 φ\varphi의 올바른 이진 전개에 대응하지 않을 수 있습니다.

이 튜토리얼에서는 Q-CTRL의 Fire Opal 오류 억제 및 성능 관리 도구를 Qiskit Function으로 사용하여 QPE 알고리즘을 구현하는 방법을 소개합니다 (Fire Opal 문서 참조). Fire Opal은 동적 디커플링, Qubit 배치 개선, 오류 억제 기법 등 고급 최적화를 자동으로 적용하여 더 높은 충실도의 결과를 도출합니다. 이러한 개선 사항은 하드웨어 비트 문자열 분포를 노이즈 없는 시뮬레이션 결과에 더욱 가깝게 만들어, 노이즈의 영향에도 불구하고 올바른 고유 위상을 신뢰성 있게 식별할 수 있게 합니다.

요구 사항

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

  • Qiskit SDK v1.4 이상 (시각화 지원 포함)
  • Qiskit Runtime v0.40 이상 (pip install qiskit-ibm-runtime)
  • Qiskit Functions Catalog v0.9.0 (pip install qiskit-ibm-catalog)
  • Fire Opal SDK v9.0.2 이상 (pip install fire-opal)
  • Q-CTRL Visualizer v8.0.2 이상 (pip install qctrl-visualizer)

설정

먼저 IBM Quantum API 키를 사용하여 인증합니다. 그런 다음 Qiskit Function을 아래와 같이 선택합니다. (이 코드는 이미 계정을 저장한 상태를 가정합니다.)

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qctrlvisualizer qiskit qiskit-aer qiskit-ibm-catalog qiskit-ibm-runtime
from qiskit import QuantumCircuit

import numpy as np
import matplotlib.pyplot as plt
import qiskit
from qiskit import qasm2
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
import qctrlvisualizer as qv
from qiskit_ibm_catalog import QiskitFunctionsCatalog

plt.style.use(qv.get_qctrl_style())
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Access Function
perf_mgmt = catalog.load("q-ctrl/performance-management")

Step 1: 고전적 입력을 양자 문제로 변환

이 튜토리얼에서는 알려진 단일 Qubit 유니터리의 고유 위상을 복원하는 QPE를 설명합니다. 위상을 추정하고자 하는 유니터리는 목표 Qubit에 적용되는 단일 Qubit 위상 Gate입니다.

U(θ)=(100eiθ)=eiθ1 ⁣1.U(\theta)= \begin{pmatrix} 1 & 0\\[2pt] 0 & e^{i\theta} \end{pmatrix} = e^{i\theta\,|1\rangle\!\langle 1|}.

고유 상태 ψ=1|\psi\rangle=|1\rangle을 준비합니다. 1|1\rangle은 고유값 eiθe^{i\theta}를 가진 U(θ)U(\theta)의 고유벡터이므로, 추정할 고유 위상은 다음과 같습니다.

φ=θ2π(mod1)\varphi = \frac{\theta}{2\pi} \pmod{1}

θ=162π\theta=\tfrac{1}{6}\cdot 2\pi로 설정하면 실제 위상은 φ=1/6\varphi=1/6이 됩니다. QPE Circuit은 제어된 거듭제곱 U2kU^{2^k}를 각도 θ2k\theta\cdot2^k의 제어된 위상 회전으로 구현하고, 계수 레지스터에 역 QFT를 적용한 후 측정합니다. 결과 비트 문자열은 1/61/6의 이진 표현 주위에 집중됩니다.

Circuit은 mm개의 계수 Qubit(추정 정밀도 설정용)과 하나의 목표 Qubit을 사용합니다. QPE 구현에 필요한 구성 요소인 양자 푸리에 변환(QFT)과 그 역변환, 고유 위상의 십진수와 이진 분수 간 변환 유틸리티 함수, 그리고 시뮬레이션과 하드웨어 결과를 비교하기 위한 원시 카운트를 확률로 정규화하는 헬퍼 함수를 정의하는 것으로 시작합니다.

def inverse_quantum_fourier_transform(quantum_circuit, number_of_qubits):
"""
Apply an inverse Quantum Fourier Transform the first `number_of_qubits` qubits in the
`quantum_circuit`.
"""
for qubit in range(number_of_qubits // 2):
quantum_circuit.swap(qubit, number_of_qubits - qubit - 1)
for j in range(number_of_qubits):
for m in range(j):
quantum_circuit.cp(-np.pi / float(2 ** (j - m)), m, j)
quantum_circuit.h(j)
return quantum_circuit
def bitstring_count_to_probabilities(data, shot_count):
"""
This function turns an unsorted dictionary of bitstring counts into a sorted dictionary
of probabilities.
"""
# Turn the bitstring counts into probabilities.
probabilities = {
bitstring: bitstring_count / shot_count
for bitstring, bitstring_count in data.items()
}

sorted_probabilities = dict(
sorted(probabilities.items(), key=lambda x: x[1], reverse=True)
)

return sorted_probabilities

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

계수 Qubit을 중첩 상태로 준비하고, 목표 고유 위상을 인코딩하기 위해 제어된 위상 회전을 적용하며, 측정 전에 역 QFT로 마무리하여 QPE Circuit을 구성합니다.

def quantum_phase_estimation_benchmark_circuit(
number_of_counting_qubits, phase
):
"""
Create the circuit for quantum phase estimation.

Parameters
----------
number_of_counting_qubits : The number of qubits in the circuit.
phase : The desired phase.

Returns
-------
QuantumCircuit
The quantum phase estimation circuit for `number_of_counting_qubits` qubits.
"""
qc = QuantumCircuit(
number_of_counting_qubits + 1, number_of_counting_qubits
)
target = number_of_counting_qubits

# |1> eigenstate for the single-qubit phase gate
qc.x(target)

# Hadamards on counting register
for q in range(number_of_counting_qubits):
qc.h(q)

# ONE controlled phase per counting qubit: cp(phase * 2**k)
for k in range(number_of_counting_qubits):
qc.cp(phase * (1 << k), k, target)

qc.barrier()

# Inverse QFT on counting register
inverse_quantum_fourier_transform(qc, number_of_counting_qubits)

qc.barrier()
for q in range(number_of_counting_qubits):
qc.measure(q, q)
return qc

Step 3: Qiskit 프리미티브를 사용한 실행

실험에 사용할 샷 수와 Qubit 수를 설정하고, mm개의 이진 자릿수를 사용하여 목표 위상 φ=1/6\varphi = 1/6을 인코딩합니다. 이 매개변수로 시뮬레이션, 기본 하드웨어, 그리고 Fire Opal 향상 Backend에서 실행할 QPE Circuit을 구성합니다.

shot_count = 10000
num_qubits = 35
phase = (1 / 6) * 2 * np.pi
circuits_quantum_phase_estimation = (
quantum_phase_estimation_benchmark_circuit(
number_of_counting_qubits=num_qubits, phase=phase
)
)

MPS 시뮬레이션 실행

먼저 matrix_product_state 시뮬레이터를 사용하여 참조 분포를 생성하고, 이후 하드웨어 결과와 비교하기 위해 카운트를 정규화된 확률로 변환합니다.

# Run the algorithm on the IBM Aer simulator.
aer_simulator = AerSimulator(method="matrix_product_state")

# Transpile the circuits for the simulator.
transpiled_circuits = qiskit.transpile(
circuits_quantum_phase_estimation, aer_simulator
)
simulated_result = (
aer_simulator.run(transpiled_circuits, shots=shot_count)
.result()
.get_counts()
)
simulated_result_probabilities = []

simulated_result_probabilities.append(
bitstring_count_to_probabilities(
simulated_result,
shot_count=shot_count,
)
)

하드웨어에서 실행

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuits = pm.run(circuits_quantum_phase_estimation)
# Run the algorithm with IBM default.
sampler = Sampler(backend)

# Run all circuits using Qiskit Runtime.
ibm_default_job = sampler.run([isa_circuits], shots=shot_count)

Fire Opal을 사용하여 하드웨어에서 실행

# Run the circuit using the sampler
fire_opal_job = perf_mgmt.run(
primitive="sampler",
pubs=[qasm2.dumps(circuits_quantum_phase_estimation)],
backend_name=backend.name,
options={"default_shots": shot_count},
)

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

# Retrieve results.
ibm_default_result = ibm_default_job.result()
ibm_default_probabilities = []

for idx, pub_result in enumerate(ibm_default_result):
ibm_default_probabilities.append(
bitstring_count_to_probabilities(
pub_result.data.c0.get_counts(),
shot_count=shot_count,
)
)
fire_opal_result = fire_opal_job.result()

fire_opal_probabilities = []
for idx, pub_result in enumerate(fire_opal_result):
fire_opal_probabilities.append(
bitstring_count_to_probabilities(
pub_result.data.c0.get_counts(),
shot_count=shot_count,
)
)
data = {
"simulation": simulated_result_probabilities,
"default": ibm_default_probabilities,
"fire_opal": fire_opal_probabilities,
}
def plot_distributions(
data,
number_of_counting_qubits,
top_k=None,
by="prob",
shot_count=None,
):
def nrm(d):
s = sum(d.values())
return {k: (v / s if s else 0.0) for k, v in d.items()}

def as_float(d):
return {k: float(v) for k, v in d.items()}

def to_space(d):
if by == "prob":
return nrm(as_float(d))
else:
if shot_count and 0.99 <= sum(d.values()) <= 1.01:
return {
k: v * float(shot_count) for k, v in as_float(d).items()
}
else:
return as_float(d)

def topk(d, k):
items = sorted(d.items(), key=lambda kv: kv[1], reverse=True)
return items[: (k or len(d))]

phase = "1/6"

sim = to_space(data["simulation"])
dft = to_space(data["default"])
qct = to_space(data["fire_opal"])

correct = max(sim, key=sim.get) if sim else None
print("Correct result:", correct)

sim_items = topk(sim, top_k)
dft_items = topk(dft, top_k)
qct_items = topk(qct, top_k)

sim_keys, y_sim = zip(*sim_items) if sim_items else ([], [])
dft_keys, y_dft = zip(*dft_items) if dft_items else ([], [])
qct_keys, y_qct = zip(*qct_items) if qct_items else ([], [])

fig, axes = plt.subplots(3, 1, layout="constrained")
ylab = "Probabilities"

def panel(ax, keys, ys, title, color):
x = np.arange(len(keys))
bars = ax.bar(x, ys, color=color)
ax.set_title(title)
ax.set_ylabel(ylab)
ax.set_xticks(x)
ax.set_xticklabels(keys, rotation=90)
ax.set_xlabel("Bitstrings")
if correct in keys:
i = keys.index(correct)
bars[i].set_edgecolor("black")
bars[i].set_linewidth(2)
return max(ys, default=0.0)

c_sim, c_dft, c_qct = (
qv.QCTRL_STYLE_COLORS[5],
qv.QCTRL_STYLE_COLORS[1],
qv.QCTRL_STYLE_COLORS[0],
)
m1 = panel(axes[0], list(sim_keys), list(y_sim), "Simulation", c_sim)
m2 = panel(axes[1], list(dft_keys), list(y_dft), "Default", c_dft)
m3 = panel(axes[2], list(qct_keys), list(y_qct), "Q-CTRL", c_qct)

for ax, m in zip(axes, (m1, m2, m3)):
ax.set_ylim(0, 1.05 * (m or 1.0))

for ax in axes:
ax.label_outer()
fig.suptitle(
rf"{number_of_counting_qubits} counting qubits, $2\pi\varphi$={phase}"
)
fig.set_size_inches(20, 10)
plt.show()
experiment_index = 0
phase_index = 0

distributions = {
"simulation": data["simulation"][phase_index],
"default": data["default"][phase_index],
"fire_opal": data["fire_opal"][phase_index],
}

plot_distributions(
distributions, num_qubits, top_k=100, by="prob", shot_count=shot_count
)
Correct result: 00101010101010101010101010101010101

이전 코드 셀의 출력 결과

시뮬레이션은 올바른 고유 위상에 대한 기준선을 설정합니다. 기본 하드웨어 실행에서는 노이즈로 인해 확률이 많은 잘못된 비트스트링에 분산되어 이 결과가 불분명하게 나타납니다. Q-CTRL Performance Management를 사용하면 분포가 더 선명해지고 올바른 결과가 복원되어, 이 규모에서 신뢰할 수 있는 QPE가 가능해집니다.

참고 문헌

[1] 강의 7: 위상 추정 및 인수분해. IBM Quantum Learning - 양자 알고리즘의 기초. 2025년 10월 3일 검색.

튜토리얼 설문

이 튜토리얼에 대한 피드백을 남겨 주시면 감사하겠습니다. 여러분의 의견은 콘텐츠 품질과 사용자 경험을 개선하는 데 도움이 됩니다.

설문 링크

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.