Q-CTRL의 성능 관리를 통한 횡자계 이징 모델
예상 사용 시간: Heron r2 프로세서에서 2분. (참고: 이는 추정치일 뿐입니다. 런타임은 다를 수 있습니다.)
배경
횡장 필드 아이싱 모델(TFIM)은 양자 자성과 위상 전이를 연구하는 데 중요한 역할을 합니다. 이는 격자 위에 배열된 일련의 스핀을 설명하며, 각 스핀은 이웃 스핀과 상호작용하는 동시에 양자 변동을 일으키는 외부 자기장의 영향을 받습니다.
이 모델을 시뮬레이션하는 일반적인 접근 방식은 트로터 분해를 사용하여 시간 진화 연산자를 근사화하여 단일 큐비트 회전과 2큐비트 상호 작용을 번갈아 가며 구성하는 회로를 구성하는 것입니다. 그러나 실제 하드웨어에서 이러한 시뮬레이션은 노이즈와 디코히어런스로 인해 실제 동역학에서 편차가 발생하기 때문에 어려움이 있습니다. 이를 극복하기 위해 Q-CTRL의 Fire Opal 오류 억제 및 성능 관리 도구는 키스킷 함수로 제공됩니다( Fire Opal 문서 참조). Fire Opal은 동적 디커플링, 고급 레이아웃, 라우팅 및 기타 오류 억제 기술을 적용하여 회로 실행을 자동으로 최적화하며, 이 모든 것이 노이즈를 줄이기 위한 것입니다. 이러한 개선으로 하드웨어 결과가 노이즈 없는 시뮬레이션과 더욱 밀접하게 일치하여 더 높은 충실도로 TFIM 자화 역학을 연구할 수 있게 되었습니다.
이 튜토리얼에서는 이렇게 하겠습니다:
- 연결된 스핀 삼각형의 그래프에 TFIM 해밀턴을 구축합니다
- 다양한 깊이의 트로터화 회로로 시간 진화 시뮬레이션
- 시간 경과에 따른 단일 큐비트 자화 계산 및 시각화
- 기준 시뮬레이션과 Q-CTRL의 Fire Opal 성능 관리를 사용한 하드웨어 실행 결과를 비교합니다
개요
횡장 아이싱 모델(TFIM)은 양자 상전이의 필수적인 특징을 포착하는 양자 스핀 모델입니다. 해밀턴은 다음과 같이 정의됩니다:
여기서 와 은 큐비트에 작용하는 폴리 연산자 , 은 인접한 스핀 사이의 결합 강도, 은 가로 자기장의 강도입니다. 첫 번째 항은 고전적인 강자성 상호작용을 나타내고, 두 번째 항은 횡단장을 통한 양자 변동을 도입합니다. TFIM 역학을 시뮬레이션하려면 연결된 스핀 삼각형의 커스텀 그래프를 기반으로 RX 및 RZZ 게이트 레이어를 통해 구현된 단일 진화 연산자 의 트로터 분해(Trotter decomposition)를 사용합니다. 이 시뮬레이션은 트로터 단계가 증가함에 따라 자화( )가 어떻게 변하는지를 살펴봅니다.
제안된 TFIM 구현의 성능은 노이즈 없는 시뮬레이션과 노이즈가 있는 백엔드를 비교하여 평가합니다. Fire Opal의 향상된 실행 및 오류 억제 기능은 실제 하드웨어에서 노이즈의 영향을 완화하는 데 사용되어 및 상관자 와 같은 스핀 관측값을 보다 안정적으로 추정할 수 있습니다.
요구사항
이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:
- 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 비주얼라이저 v8.0.2 이상 (
pip install qctrl-visualizer)
설정
먼저, IBM Quantum API 키 를 사용하여 인증하십시오. 그런 다음 다음과 같이 Qiskit 함수를 선택합니다. (이 코드는 사용자가 이미 계정을 로컬 환경에 저장해 두었다고 가정합니다.)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit.quantum_info import SparsePauliOp
from qiskit_aer import AerSimulator
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import qctrlvisualizer as qvcatalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Access Function
perf_mgmt = catalog.load("q-ctrl/performance-management")1단계: 고전적 입력을 양자 문제에 매핑하기
TFIM 그래프 생성
먼저 스핀 격자와 그 사이의 결합을 정의하는 것으로 시작합니다. 이 튜토리얼에서는 선형 체인으로 배열된 연결된 삼각형으로 격자를 구성합니다. 각 삼각형은 폐쇄 루프로 연결된 세 개의 노드로 구성되며, 각 삼각형의 한 노드를 이전 삼각형에 연결하여 체인을 형성합니다.
도우미 함수 connected_triangles_adj_matrix 는 이 구조에 대한 인접 행렬을 작성합니다. 삼각형 체인의 경우 결과 그래프에 노드가 포함됩니다.
def connected_triangles_adj_matrix(n):
"""
Generate the adjacency matrix for 'n' connected triangles in a chain.
"""
num_nodes = 2 * n + 1
adj_matrix = np.zeros((num_nodes, num_nodes), dtype=int)
for i in range(n):
a, b, c = i * 2, i * 2 + 1, i * 2 + 2 # Nodes of the current triangle
# Connect the three nodes in a triangle
adj_matrix[a, b] = adj_matrix[b, a] = 1
adj_matrix[b, c] = adj_matrix[c, b] = 1
adj_matrix[a, c] = adj_matrix[c, a] = 1
# If not the first triangle, connect to the previous triangle
if i > 0:
adj_matrix[a, a - 1] = adj_matrix[a - 1, a] = 1
return adj_matrix방금 정의한 격자를 시각화하기 위해 연결된 삼각형의 사슬을 플로팅하고 각 노드에 레이블을 지정할 수 있습니다. 아래 함수는 선택한 수의 트라이앵글에 대한 그래프를 작성하여 표시합니다.
def plot_triangle_chain(n, side=1.0):
"""
Plot a horizontal chain of n equilateral triangles.
Baseline: even nodes (0,2,4,...,2n) on y=0
Apexes: odd nodes (1,3,5,...,2n-1) above the midpoint.
"""
# Build graph
A = connected_triangles_adj_matrix(n)
G = nx.from_numpy_array(A)
h = np.sqrt(3) / 2 * side
pos = {}
# Place baseline nodes
for k in range(n + 1):
pos[2 * k] = (k * side, 0.0)
# Place apex nodes
for k in range(n):
x_left = pos[2 * k][0]
x_right = pos[2 * k + 2][0]
pos[2 * k + 1] = ((x_left + x_right) / 2, h)
# Draw
fig, ax = plt.subplots(figsize=(1.5 * n, 2.5))
nx.draw(
G,
pos,
ax=ax,
with_labels=True,
font_size=10,
font_color="white",
node_size=600,
node_color=qv.QCTRL_STYLE_COLORS[0],
edge_color="black",
width=2,
)
ax.set_aspect("equal")
ax.margins(0.2)
plt.show()
return G, pos이 튜토리얼에서는 20개의 삼각형 체인을 사용하겠습니다.
n_triangles = 20
n_qubits = 2 * n_triangles + 1
plot_triangle_chain(n_triangles, side=1.0)
plt.show()Output:
그래프 변 채우기
스핀-스핀 결합을 구현하려면 겹치지 않는 가장자리를 그룹화하는 것이 유용합니다. 이를 통해 2큐비트 게이트를 병렬로 적용할 수 있습니다. 각 에지에 색상을 할당하여 같은 노드에서 만나는 에지가 서로 다른 그룹에 배치되도록 하는 간단한 에지 색상 지정 절차 [1] 를 통해 이를 수행할 수 있습니다.
def edge_coloring(graph):
"""
Takes a NetworkX graph and returns a list of lists
where each inner list contains
the edges assigned the same color.
"""
line_graph = nx.line_graph(graph)
edge_colors = nx.coloring.greedy_color(line_graph)
color_groups = {}
for edge, color in edge_colors.items():
if color not in color_groups:
color_groups[color] = []
color_groups[color].append(edge)
return list(color_groups.values())2단계: 양자 하드웨어 실행을 위한 문제 최적화
스핀 그래프 상에서 Trotterized 회로를 생성한다
TFIM의 동역학을 시뮬레이션하기 위해 시간 진화 연산자를 근사화하는 회로를 구성합니다.
2차 트로터 분해를 사용합니다:
및 .
- 용어는
RX로테이션의 레이어로 구현됩니다. - 용어는 인터랙션 그래프의 가장자리를 따라
RZZ게이트 레이어로 구현됩니다.
이러한 게이트의 각도는 횡방향 필드 , 결합 상수 , 시간 단계 에 의해 결정됩니다. 여러 트로터 단계를 쌓아 시스템의 동역학에 근사한 깊이가 증가하는 회로를 생성합니다. generate_tfim_circ_custom_graph 및 trotter_circuits 함수는 임의의 스핀 상호작용 그래프로부터 트로터라이즈드 양자 회로를 구성합니다.
def generate_tfim_circ_custom_graph(
steps, h, J, dt, psi0, graph: nx.graph.Graph, meas_basis="Z", mirror=False
):
"""
Generate a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2)
for simulating a transverse field ising model:
e^{-i H t} where the Hamiltonian H = -J \\sum_i Z_i Z_{i+1} + h \\sum_i X_i.
steps: Number of trotter steps
theta_x: Angle for layer of X rotations
theta_zz: Angle for layer of ZZ rotations
theta_x: Angle for second layer of X rotations
J: Coupling between nearest neighbor spins
h: The transverse magnetic field strength
dt: t/total_steps
psi0: initial state (assumed to be prepared in the computational basis).
meas_basis: basis to measure all correlators in
This is a second order trotter of the form e^(a+b) ~ e^(b/2) e^a e^(b/2)
"""
theta_x = h * dt
theta_zz = -2 * J * dt
nq = graph.number_of_nodes()
color_edges = edge_coloring(graph)
circ = QuantumCircuit(nq, nq)
# Initial state, for typical cases in the computational basis
for i, b in enumerate(psi0):
if b == "1":
circ.x(i)
# Trotter steps
for step in range(steps):
for i in range(nq):
circ.rx(theta_x, i)
if mirror:
color_edges = [sublist[::-1] for sublist in color_edges[::-1]]
for edge_list in color_edges:
for edge in edge_list:
circ.rzz(theta_zz, edge[0], edge[1])
for i in range(nq):
circ.rx(theta_x, i)
# some typically used basis rotations
if meas_basis == "X":
for b in range(nq):
circ.h(b)
elif meas_basis == "Y":
for b in range(nq):
circ.sdg(b)
circ.h(b)
for i in range(nq):
circ.measure(i, i)
return circ
def trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, mirror=True):
"""
Generates a sequence of Trotterized circuits, each with increasing depth.
Given a spin interaction graph and Hamiltonian parameters, it constructs
a list of circuits with 1 to d_ind_tot Trotter steps
G: Graph defining spin interactions (edges = ZZ couplings)
d_ind_tot: Number of Trotter steps (maximum depth)
J: Coupling between nearest neighboring spins
h: Transverse magnetic field strength
dt: (t / total_steps
meas_basis: Basis to measure all correlators in
mirror: If True, mirror the Trotter layers
"""
qubit_count = len(G)
circuits = []
psi0 = "0" * qubit_count
for steps in range(1, d_ind_tot + 1):
circuits.append(
generate_tfim_circ_custom_graph(
steps, h, J, dt, psi0, G, meas_basis, mirror
)
)
return circuits단일 큐비트 자화 추정
모델의 역학을 연구하기 위해 기대값 으로 정의된 각 큐비트의 자화를 측정하고자 합니다.
시뮬레이션에서는 측정 결과에서 직접 이를 계산할 수 있습니다. z_expectation 함수는 비트 문자열 수를 처리하고 선택한 큐비트 인덱스에 대해 값을 반환합니다. 실제 하드웨어에서는 generate_z_observables 함수를 사용하여 폴리 연산자를 지정하여 동일한 수량을 평가한 다음 백엔드에서 기대값을 계산합니다.
def z_expectation(counts, index):
"""
counts: Dict of mitigated bitstrings.
index: Index i in the single operator expectation value < II...Z_i...I >
to be calculated.
return: < Z_i >
"""
z_exp = 0
tot = 0
for bitstring, value in counts.items():
bit = int(bitstring[index])
sign = 1
if bit % 2 == 1:
sign = -1
z_exp += sign * value
tot += value
return z_exp / totdef generate_z_observables(nq):
observables = []
for i in range(nq):
pauli_string = "".join(["Z" if j == i else "I" for j in range(nq)])
observables.append(SparsePauliOp(pauli_string))
return observablesobservables = generate_z_observables(n_qubits)이제 트로터화된 회로를 생성하기 위한 파라미터를 정의합니다. 이 튜토리얼에서 격자는 20개의 삼각형이 연결된 체인으로, 41큐비트 시스템에 해당합니다.
all_circs_mirror = []
for num_triangles in [n_triangles]:
for meas_basis in ["Z"]:
A = connected_triangles_adj_matrix(num_triangles)
G = nx.from_numpy_array(A)
nq = len(G)
d_ind_tot = 22
dt = 2 * np.pi * 1 / 30 * 0.25
J = 1
h = -7
all_circs_mirror.extend(
trotter_circuits(G, d_ind_tot, J, h, dt, meas_basis, True)
)
circs = all_circs_mirror3단계: Qiskit primitives 명령어로 실행합니다
MPS 시뮬레이션 실행
트로터화 회로 목록은 matrix_product_state 시뮬레이터를 사용하여 샷을 임의로 선택하여 실행합니다. MPS 방법은 선택한 결합 치수에 따라 정확도가 결정되는 회로 역학에 대한 효율적인 근사치를 제공합니다. 여기서 고려하는 시스템 크기의 경우, 기본 결합 치수는 자화 역학을 충실도 높게 캡처하기에 충분합니다. 원시 카운트는 정규화되며, 이로부터 각 트로터 단계에서 단일 큐비트 기대값 을 계산합니다. 마지막으로 모든 큐비트에 대한 평균을 계산하여 자화가 시간에 따라 어떻게 변화하는지 보여주는 단일 곡선을 얻습니다.
backend_sim = AerSimulator(method="matrix_product_state")
def normalize_counts(counts_list, shots):
new_counts_list = []
for counts in counts_list:
a = {k: v / shots for k, v in counts.items()}
new_counts_list.append(a)
return new_counts_list
def run_sim(circ_list):
shots = 4096
res = backend_sim.run(circ_list, shots=shots)
normed = normalize_counts(res.result().get_counts(), shots)
return normed
sim_counts = run_sim(circs)하드웨어에서 실행
service = QiskitRuntimeService()
backend = service.backend("ibm_marrakesh")
def run_qiskit(circ_list):
shots = 4096
pm = generate_preset_pass_manager(backend=backend)
isa_circuits = [pm.run(qc) for qc in circ_list]
sampler = Sampler(mode=backend)
res = sampler.run(isa_circuits, shots=shots)
res = [r.data.c.get_counts() for r in res.result()]
normed = normalize_counts(res, shots)
return normed
qiskit_counts = run_qiskit(circs)Fire Opal이 탑재된 하드웨어에서 실행
우리는 실제 양자 하드웨어에서 자화 동역학을 평가한다. Fire Opal은 표준 ‘ IBM Quantum ’ 추정기 기본 기능을 확장하여 자동 오류 억제 및 성능 관리 기능을 제공하는 Qiskit 함수를 제공합니다. 우리는 트로터화된 회로를 IBM® 백엔드에 직접 제출하고, Fire Opal이 노이즈를 고려한 실행을 처리합니다.
pubs 의 목록을 준비하며, 각 항목에는 회로와 해당 Pauli-Z 관측값이 포함되어 있습니다. 이 값은 각 트로터 단계에서 각 큐비트에 대한 기대값 을 반환하는 Fire Opal의 추정기 함수에 전달됩니다. 그런 다음 큐비트에 대한 결과를 평균화하여 하드웨어에서 자화 곡선을 얻을 수 있습니다.
backend_name = "ibm_marrakesh"
estimator_pubs = [(qc, observables) for qc in all_circs_mirror[:]]
# Run the circuit using the estimator
qctrl_estimator_job = perf_mgmt.run(
primitive="estimator",
pubs=estimator_pubs,
backend_name=backend_name,
options={"default_shots": 4096},
)
result_qctrl = qctrl_estimator_job.result()4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환
마지막으로 시뮬레이터의 자화 곡선을 실제 하드웨어에서 얻은 결과와 비교합니다. 두 가지를 나란히 플롯하면 파이어 오팔을 사용한 하드웨어 실행이 트로터 단계의 노이즈 없는 기준선과 얼마나 근접하게 일치하는지 알 수 있습니다.
def make_correlators(test_counts, nq, d_ind_tot):
mz = np.empty((nq, d_ind_tot))
for d_ind in range(d_ind_tot):
counts = test_counts[d_ind]
for i in range(nq):
mz[i, d_ind] = z_expectation(counts, i)
average_z = np.mean(mz, axis=0)
return np.concatenate((np.array([1]), average_z), axis=0)
sim_exp = make_correlators(sim_counts[0:22], nq=nq, d_ind_tot=22)
qiskit_exp = make_correlators(qiskit_counts[0:22], nq=nq, d_ind_tot=22)qctrl_exp = [ev.data.evs for ev in result_qctrl[:]]
qctrl_exp_mean = np.concatenate(
(np.array([1]), np.mean(qctrl_exp, axis=1)), axis=0
)def make_expectations_plot(
sim_z,
depths,
exp_qctrl=None,
exp_qctrl_error=None,
exp_qiskit=None,
exp_qiskit_error=None,
plot_from=0,
plot_upto=23,
):
import numpy as np
import matplotlib.pyplot as plt
depth_ticks = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]
d = np.asarray(depths)[plot_from:plot_upto]
sim = np.asarray(sim_z)[plot_from:plot_upto]
qk = (
None
if exp_qiskit is None
else np.asarray(exp_qiskit)[plot_from:plot_upto]
)
qc = (
None
if exp_qctrl is None
else np.asarray(exp_qctrl)[plot_from:plot_upto]
)
qk_err = (
None
if exp_qiskit_error is None
else np.asarray(exp_qiskit_error)[plot_from:plot_upto]
)
qc_err = (
None
if exp_qctrl_error is None
else np.asarray(exp_qctrl_error)[plot_from:plot_upto]
)
# ---- helper(s) ----
def rmse(a, b):
if a is None or b is None:
return None
a = np.asarray(a, dtype=float)
b = np.asarray(b, dtype=float)
mask = np.isfinite(a) & np.isfinite(b)
if not np.any(mask):
return None
diff = a[mask] - b[mask]
return float(np.sqrt(np.mean(diff**2)))
def plot_panel(ax, method_y, method_err, color, label, band_color=None):
# Noiseless reference
ax.plot(d, sim, color="grey", label="Noiseless simulation")
# Method line + band
if method_y is not None:
ax.plot(d, method_y, color=color, label=label)
if method_err is not None:
lo = np.clip(method_y - method_err, -1.05, 1.05)
hi = np.clip(method_y + method_err, -1.05, 1.05)
ax.fill_between(
d,
lo,
hi,
alpha=0.18,
color=band_color if band_color else color,
label=f"{label} ± error",
)
else:
ax.text(
0.5,
0.5,
"No data",
transform=ax.transAxes,
ha="center",
va="center",
fontsize=10,
color="0.4",
)
# RMSE box (vs sim)
r = rmse(method_y, sim)
if r is not None:
ax.text(
0.98,
0.02,
f"RMSE: {r:.4f}",
transform=ax.transAxes,
va="bottom",
ha="right",
fontsize=8,
bbox=dict(
boxstyle="round,pad=0.35", fc="white", ec="0.7", alpha=0.9
),
)
# Axes
ax.set_xticks(depth_ticks)
ax.set_ylim(-1.05, 1.05)
ax.grid(True, which="both", linewidth=0.4, alpha=0.4)
ax.set_axisbelow(True)
ax.legend(prop={"size": 8}, loc="best")
fig, axes = plt.subplots(1, 2, figsize=(10, 4), dpi=300, sharey=True)
axes[0].set_title("Fire Opal (Q-CTRL)", fontsize=10)
plot_panel(
axes[0],
qc,
qc_err,
color="#680CE9",
label="Fire Opal",
band_color="#680CE9",
)
axes[0].set_xlabel("Trotter step")
axes[0].set_ylabel(r"$\langle Z \rangle$")
axes[1].set_title("Qiskit", fontsize=10)
plot_panel(
axes[1], qk, qk_err, color="blue", label="Qiskit", band_color="blue"
)
axes[1].set_xlabel("Trotter step")
plt.tight_layout()
plt.show()depths = list(range(d_ind_tot + 1))
errors = np.abs(np.array(qctrl_exp_mean) - np.array(sim_exp))
errors_qiskit = np.abs(np.array(qiskit_exp) - np.array(sim_exp))make_expectations_plot(
sim_exp,
depths,
exp_qctrl=qctrl_exp_mean,
exp_qctrl_error=errors,
exp_qiskit=qiskit_exp,
exp_qiskit_error=errors_qiskit,
)Output:
참조
[1] 그래프 색칠하기. Wikipedia. 검색된 날짜: 2025년 9월 15일 https://en.wikipedia.org/wiki/Graph _coloring
튜토리얼 설문조사
잠시 시간을 내어 이 튜토리얼에 대한 피드백을 제공해 주세요. 여러분의 인사이트는 콘텐츠 제공과 사용자 경험을 개선하는 데 도움이 됩니다.