주 콘텐츠로 건너뛰기

잘린 Bell 쌍으로 동적 Circuit 벤치마크하기

사용량 추정치: Heron r2 프로세서에서 22초 (참고: 이는 추정치일 뿐이며, 실제 실행 시간은 다를 수 있습니다.)

배경

양자 하드웨어는 일반적으로 로컬 상호작용으로 제한되어 있지만, 많은 알고리즘은 멀리 떨어진 Qubit, 심지어 별도의 프로세서에 있는 Qubit를 얽히게 해야 합니다. 동적 Circuit, 즉 회로 중간 측정 및 피드포워드가 포함된 Circuit은, 실시간 고전 통신을 사용하여 비로컬 양자 연산을 효과적으로 구현함으로써 이러한 제한을 극복하는 방법을 제공합니다. 이 접근 방식에서, Circuit의 한 부분(또는 하나의 QPU)에서의 측정 결과가 다른 부분의 Gate를 조건부로 트리거할 수 있어, 장거리에 걸쳐 얽힘을 텔레포트할 수 있습니다. 이것이 로컬 연산 및 고전 통신(LOCC) 방식의 기반을 형성하며, 여기서 우리는 얽힌 자원 상태(Bell 쌍)를 소비하고 측정 결과를 고전적으로 통신하여 멀리 떨어진 Qubit을 연결합니다.

LOCC의 유망한 활용 중 하나는 텔레포테이션을 통해 가상의 장거리 CNOT Gate를 구현하는 것으로, 장거리 얽힘 튜토리얼에 나와 있습니다. 직접적인 장거리 CNOT(하드웨어 연결성이 허용하지 않을 수 있음) 대신, Bell 쌍을 생성하고 텔레포테이션 기반 Gate 구현을 수행합니다. 그러나 이러한 연산의 충실도는 하드웨어 특성에 따라 달라집니다. 측정 결과를 기다리는 동안 필요한 지연 시간 동안의 Qubit 결맞음 손실과 고전 통신 지연 시간이 얽힌 상태를 저하시킬 수 있습니다. 또한, 회로 중간 측정의 오류는 조건부 Gate를 통해 Circuit의 나머지 부분으로 전파되기 때문에 최종 측정의 오류보다 수정하기가 더 어렵습니다.

참고 실험에서 저자들은 장치의 어떤 부분이 LOCC 기반 얽힘에 가장 적합한지 식별하기 위한 Bell 쌍 충실도 벤치마크를 소개합니다. 아이디어는 프로세서에서 연결된 4개의 Qubit 그룹마다 소규모 동적 Circuit을 실행하는 것입니다. 이 4-Qubit Circuit은 먼저 두 중간 Qubit에 Bell 쌍을 생성한 다음, LOCC를 사용하여 두 에지 Qubit을 얽히게 하는 자원으로 사용합니다. 구체적으로, Qubit 1과 2는 로컬로(Hadamard와 CNOT 사용) 잘리지 않은 Bell 쌍으로 준비되고, 텔레포테이션 루틴이 해당 Bell 쌍을 소비하여 Qubit 0과 3을 얽히게 합니다. Qubit 1과 2는 Circuit 실행 중에 측정되며, 그 결과에 따라 Pauli 수정(Qubit 3에 X, Qubit 0에 Z)이 적용됩니다. 그러면 Qubit 0과 3은 Circuit 끝에 Bell 상태로 남게 됩니다.

이 최종 얽힌 쌍의 품질을 수량화하기 위해, 안정자를 측정합니다. 구체적으로 ZZ 기저에서의 패리티(Z0Z3Z_0Z_3)와 XX 기저에서의 패리티(X0X3X_0X_3)입니다. 완벽한 Bell 쌍의 경우, 이 기댓값들은 모두 +1입니다. 실제로는 하드웨어 노이즈가 이 값들을 감소시킵니다. 따라서 각 Qubit 쌍에 대해 Circuit을 두 번 반복합니다. 하나의 Circuit은 Qubit 0과 3을 ZZ 기저에서 측정하고, 다른 하나는 XX 기저에서 측정합니다. 결과로부터 해당 Qubit 쌍에 대한 Z0Z3\langle Z_0Z_3\rangleX0X3\langle X_0X_3\rangle의 추정값을 얻습니다. 이상적인 값(1)에 대한 이 안정자들의 평균 제곱 오차(MSE)를 얽힘 충실도의 간단한 지표로 사용합니다. MSE가 낮을수록 두 Qubit이 이상적인 Bell 상태에 더 가깝게 도달했음을(충실도가 더 높음)을 의미하며, MSE가 높을수록 더 많은 오류가 있음을 나타냅니다. 이 실험을 장치 전체에 걸쳐 수행함으로써, 다양한 Qubit 그룹의 측정-피드포워드 기능을 벤치마크하고 LOCC 연산에 가장 적합한 Qubit 쌍을 식별할 수 있습니다.

이 튜토리얼은 동적 Circuit이 어떻게 멀리 떨어진 Qubit 간의 얽힘을 생성하고 평가하는 데 사용될 수 있는지 보여주기 위해 IBM Quantum® 장치에서 실험을 시연합니다. 장치의 모든 4-Qubit 선형 체인을 매핑하고, 각각에 대해 텔레포테이션 Circuit을 실행한 다음, MSE 값의 분포를 시각화합니다. 이 엔드-투-엔드 절차는 Qiskit Runtime과 동적 Circuit 기능을 활용하여 Circuit을 절단하거나 양자 알고리즘을 모듈식 시스템에 분산할 때 하드웨어를 고려한 선택을 위한 정보를 얻는 방법을 보여줍니다.

요구 사항

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

  • Qiskit SDK v2.0 이상, 시각화 지원 포함
  • Qiskit Runtime v0.40 이상 (pip install qiskit-ibm-runtime)

설정

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

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler import generate_preset_pass_manager

import numpy as np
import matplotlib.pyplot as plt

def create_bell_stab(initial_layouts):
"""
Create a circuit for a 1D chain of qubits (number of qubits must be a multiple of 4),
where a middle Bell pair is consumed to create a Bell at the edge.
Takes as input a list of lists, where each element of the list is a
1D chain of physical qubits that is used as the initial_layout for the transpiled circuit.
Returns a list of length-2 tuples, each tuple contains a circuit to measure the ZZ stabilizer and
a circuit to measure the XX stabilizer of the edge Bell state.
"""
bell_circuits = []
for (
initial_layout
) in initial_layouts: # Iterate over chains of physical qubits
assert (
len(initial_layout) % 4 == 0
), f"The length of the chain must be a multiple of 4, len(inital_layout)={len(initial_layout)}"
num_pairs = len(initial_layout) // 4

bell_parallel = QuantumCircuit(4 * num_pairs, 4 * num_pairs)

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.h(q0)
bell_parallel.h(q1)
bell_parallel.cx(q1, q2)
bell_parallel.cx(q0, q1)
bell_parallel.cx(q2, q3)
bell_parallel.h(q2)

# add barrier BEFORE measurements and add id in conditional
bell_parallel.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits

bell_parallel.measure(q1, ca0)
bell_parallel.measure(q2, ca1)
# bell_parallel.barrier() #remove barrier after measurement

for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(ca0, ca1) = pair_idx * 4 + 1, pair_idx * 4 + 2 # middle qubits
with bell_parallel.if_test((ca0, 1)):
bell_parallel.x(q3)
with bell_parallel.if_test((ca1, 1)):
bell_parallel.z(q0)
bell_parallel.id(q0) # add id here for correct alignment

bell_zz = bell_parallel.copy()
bell_zz.barrier()
bell_xx = bell_parallel.copy()
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
bell_xx.h(q0)
bell_xx.h(q3)
bell_xx.barrier()
for pair_idx in range(num_pairs):
(q0, q1, q2, q3) = (
pair_idx * 4,
pair_idx * 4 + 1,
pair_idx * 4 + 2,
pair_idx * 4 + 3,
)
(c0, c1) = pair_idx * 4, pair_idx * 4 + 3 # edge qubits

bell_zz.measure(q0, c0)
bell_zz.measure(q3, c1)

bell_xx.measure(q0, c0)
bell_xx.measure(q3, c1)

bell_circuits.append(bell_zz)
bell_circuits.append(bell_xx)

return bell_circuits

def get_mse(result, initial_layouts):
"""
given a result object and the initial layouts, returns a dict of layouts and their mse
"""
layout_mse = {}
for layout_idx, initial_layout in enumerate(initial_layouts):
layout_mse[tuple(initial_layout)] = {}

num_pairs = len(initial_layout) // 4

counts_zz = result[2 * layout_idx].data.c.get_counts()
total_shots = sum(counts_zz.values())

# Get ZZ expectation value
exp_zz_list = []
for pair_idx in range(num_pairs):
exp_zz = 0
for bitstr, shots in counts_zz.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
z_val0 = 1 if b0 == "0" else -1
z_val1 = 1 if b1 == "0" else -1
exp_zz += z_val0 * z_val1 * shots
exp_zz /= total_shots
exp_zz_list.append(exp_zz)

counts_xx = result[2 * layout_idx + 1].data.c.get_counts()
total_shots = sum(counts_xx.values())

# Get XX expectation value
exp_xx_list = []
for pair_idx in range(num_pairs):
exp_xx = 0
for bitstr, shots in counts_xx.items():
bitstr = bitstr[::-1] # reverse order to big endian
b1, b0 = (
bitstr[pair_idx * 4],
bitstr[pair_idx * 4 + 3],
) # parse bitstring to get edge measurements for each 4-q chain
x_val0 = 1 if b0 == "0" else -1
x_val1 = 1 if b1 == "0" else -1
exp_xx += x_val0 * x_val1 * shots
exp_xx /= total_shots
exp_xx_list.append(exp_xx)

mse_list = [
((exp_zz - 1) ** 2 + (exp_xx - 1) ** 2) / 2
for exp_zz, exp_xx in zip(exp_zz_list, exp_xx_list)
]

print(f"layout {initial_layout}")
for idx in range(num_pairs):
layout_mse[tuple(initial_layout)][
tuple(initial_layout[4 * idx : 4 * idx + 4])
] = mse_list[idx]
print(
f"qubits: {initial_layout[4*idx:4*idx+4]}, mse:, {round(mse_list[idx],4)}"
)
# print(f'exp_zz: {round(exp_zz_list[idx],4)}, exp_xx: {round(exp_xx_list[idx],4)}')
print(" ")
return layout_mse

def plot_mse_ecdfs(layouts_mse, combine_layouts=False):
"""
Plot CDF of MSE data for multiple layouts. Optionally combine all data in a single CDF
"""

if not combine_layouts:
for initial_layout, layouts in layouts_mse.items():
sorted_layouts = dict(
sorted(layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {initial_layout}",
)

# add qubits labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

elif combine_layouts:
all_layouts = {}
all_initial_layout = []
for (
initial_layout,
layouts,
) in layouts_mse.items(): # puts together all layout information
all_layouts.update(layouts)
all_initial_layout += initial_layout

sorted_layouts = dict(
sorted(all_layouts.items(), key=lambda item: item[1])
) # sort layouts by mse

# get layouts and mses
layout_list = list(sorted_layouts.keys())
mse_list = np.asarray(list(sorted_layouts.values()))

# convert to numpy
x = np.array(mse_list)
y = np.arange(1, len(x) + 1) / len(x)

# Prepend (x[0], 0) to start CDF at zero
x = np.insert(x, 0, x[0])
y = np.insert(y, 0, 0)

# Create the plot
plt.plot(
x,
y,
marker="x",
linestyle="-",
label=f"qubits: {sorted(list(set(all_initial_layout)))}",
)

# add qubit labels for the edge pairs
for xi, yi, q in zip(x[1:], y[1:], layout_list):
plt.annotate(
[q[0], q[3]],
(xi, yi),
textcoords="offset points",
xytext=(5, -10),
ha="left",
fontsize=8,
)

plt.xscale("log")
plt.xlabel("Mean squared error of ⟨ZZ⟩ and ⟨XX⟩")
plt.ylabel("Cumulative distribution function")
plt.title("CDF for different initial layouts")
plt.grid(alpha=0.3)
plt.show()

1단계: 고전적 입력을 양자 문제로 매핑하기

첫 번째 단계는 장치의 토폴로지에 맞춰 모든 후보 Bell-pair 링크를 벤치마킹할 양자 Circuit 집합을 생성하는 것입니다. 장치 커플링 맵에서 선형으로 연결된 네 Qubit 체인을 프로그래밍 방식으로 탐색합니다. 이러한 각 체인(Qubit 인덱스 [q0q1q2q3][q0-q1-q2-q3]으로 레이블링됨)은 얽힘 교환 Circuit의 테스트 케이스 역할을 합니다. 길이 4의 가능한 모든 경로를 식별함으로써, 프로토콜을 실현할 수 있는 Qubit 그룹화에 대한 최대 커버리지를 보장합니다.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True)

이러한 체인은 장치 그래프에 대해 탐욕적(greedy) 탐색을 수행하는 헬퍼 함수를 사용하여 생성합니다. 이 함수는 네 개의 4-Qubit 체인을 묶은 16-Qubit 그룹인 "스트라이프(stripe)"를 반환합니다(동적 Circuit은 현재 측정 레지스터의 크기를 16 Qubit으로 제한합니다). 번들링을 통해 칩의 서로 다른 부분에서 여러 4-Qubit 실험을 병렬로 실행하고 전체 장치를 효율적으로 활용할 수 있습니다. 각 16-Qubit 스트라이프에는 네 개의 분리된(disjoint) 체인이 포함되어 있으며, 이는 해당 그룹 내에서 어떤 Qubit도 재사용되지 않음을 의미합니다. 예를 들어, 하나의 스트라이프는 [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11], [12131415][12-13-14-15] 체인이 함께 묶인 형태일 수 있습니다. 스트라이프에 포함되지 않은 Qubit은 leftover 변수로 반환됩니다.

from itertools import chain
from collections import defaultdict

def stripes16_from_backend(backend):
"""
Creates stripes of 16 qubits, four non-overlapping four-qubit chains, that cover as much of
the coupling map as possible. Returns any unused qubits as leftovers.
"""
# get the undirected adjacency list
edges = backend.coupling_map.get_edges()
graph = defaultdict(set)
for u, v in edges:
graph[u].add(v)
graph[v].add(u)

qubits = sorted(graph) # all qubit indices that appear

# greedy search for 4-long linear chains (blocks) ────────────
used = set() # qubits already placed in a block
blocks = [] # each block is a four-qubit list

for q in qubits: # deterministic order for reproducibility
if q in used:
continue # already consumed by earlier block

# depth-first "straight" walk of length 3 without revisiting nodes
def extend(path):
if len(path) == 4:
return path
tip = path[-1]
for nbr in sorted(graph[tip]): # deterministic
if nbr not in path and nbr not in used:
maybe = extend(path + [nbr])
if maybe:
return maybe
return None

block = extend([q])
if block: # found a 4-node path
blocks.append(block)
used.update(block)

# bundle four four-qubit blocks into one 16-qubit stripe (max number of measurement compatible with if-else)
stripes = [
list(chain.from_iterable(blocks[i : i + 4]))
for i in range(0, len(blocks) // 4 * 4, 4) # full groups of four
]

leftovers = set(qubits) - set(chain.from_iterable(stripes))
return stripes, leftovers
initial_layouts, leftover = stripes16_from_backend(backend)

다음으로, 각 16-Qubit 스트라이프에 대한 Circuit을 구성합니다. 각 체인에 대해 루틴은 다음을 수행합니다.

  • 중간 Bell 쌍 준비: Qubit 1에 Hadamard Gate를 적용하고, Qubit 1에서 Qubit 2로 CNOT Gate를 적용합니다. 이렇게 하면 Qubit 1과 2가 얽혀 Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2} Bell 상태가 만들어집니다.
  • 가장자리 Qubit 얽기: Qubit 0에서 Qubit 1로, Qubit 2에서 Qubit 3으로 CNOT Gate를 적용합니다. 이를 통해 처음에는 분리되어 있던 쌍들이 연결되어, 다음 단계 이후 Qubit 0과 3이 얽히게 됩니다. Qubit 2에도 Hadamard Gate가 적용됩니다(이는 이전 CNOT들과 결합하여 Qubit 1과 2에 대한 Bell 측정의 일부를 형성합니다). 이 시점에서 Qubit 0과 3은 아직 얽혀 있지 않지만, Qubit 1과 2는 더 큰 4-Qubit 상태에서 이들과 얽혀 있습니다.
  • 회로 중간 측정 및 피드포워드: 중간 Qubit인 1과 2를 계산 기저에서 측정하여 두 개의 고전 비트를 얻습니다. 이 측정 결과에 따라 조건부 연산을 적용합니다. Qubit 1의 측정 결과(이를 비트 m12m_{12}라 함)가 1이면 Qubit 3에 XX Gate를 적용하고, Qubit 2의 측정 결과(m21m_{21})가 1이면 Qubit 0에 ZZ Gate를 적용합니다. Qiskit의 if_test/if_else 구조를 사용하여 구현되는 이러한 조건부 Gate는 표준 텔레포테이션 보정을 구현합니다. 이를 통해 Qubit 1과 2를 투영함으로써 발생하는 임의의 Pauli 뒤집힘을 "되돌려서", 측정 결과에 관계없이 Qubit 0과 3이 알려진 Bell 상태에 놓이도록 보장합니다. 이 단계 이후, Qubit 0과 3은 이상적으로 Bell 상태 Φ+|\Phi^+\rangle에서 얽혀 있어야 합니다.
  • Bell 쌍 안정자 측정: 그런 다음 Circuit을 두 가지 버전으로 분리합니다. 첫 번째 버전에서는 Qubit 0과 3에서 ZZZZ 안정자를 측정합니다. 두 번째 버전에서는 이 Qubit들에서 XXXX 안정자를 측정합니다.

각 4-Qubit 초기 레이아웃에 대해, 위 함수는 두 개의 Circuit(ZZZZ 측정용과 XXXX 안정자 측정용)을 반환합니다. 이 단계가 끝나면, 장치의 모든 4-Qubit 체인을 포함하는 Circuit 목록이 생성됩니다. 이 Circuit에는 회로 중간 측정과 조건부(if/else) 연산이 포함되어 있으며, 이것이 동적 Circuit의 핵심 명령입니다.

circuits = create_bell_stab(initial_layouts)
circuits[-1].draw("mpl", fold=-1)

Output of the previous code cell

2단계: 양자 하드웨어 실행을 위해 문제 최적화하기

실제 하드웨어에서 Circuit을 실행하기 전에, 장치의 물리적 제약에 맞게 트랜스파일해야 합니다. Transpiler는 추상 Circuit을 선택한 장치의 물리적 Qubit과 Gate 집합에 매핑합니다. 각 체인에 특정 물리적 Qubit을 이미 선택했으므로(Circuit 생성기에 initial_layout을 제공함으로써), 고정된 레이아웃으로 Transpiler의 optimization_level=0을 사용합니다. 이는 Qiskit에게 Qubit을 재할당하거나 Circuit 구조를 변경할 수 있는 무거운 최적화를 수행하지 말라고 지시합니다. 특히 조건부 Gate의 연산 순서를 정확히 지정된 대로 유지하려 합니다.

isa_circuits = []
for ind, init_layout in enumerate(initial_layouts):
pm = generate_preset_pass_manager(
optimization_level=0, backend=backend, initial_layout=init_layout
)
isa_circ = pm.run(circuits[ind * 2 : ind * 2 + 2])
isa_circuits.extend(isa_circ)
isa_circuits[1].draw("mpl", fold=-1, idle_wires=False)

Output of the previous code cell

3단계: Qiskit 프리미티브를 사용하여 실행하기

이제 양자 장치에서 실험을 실행할 수 있습니다. Qiskit Runtime과 그 Sampler 프리미티브를 사용하여 Circuit 배치를 효율적으로 실행합니다.

sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["cut-bell-pair-test"]
job = sampler.run(isa_circuits)

4단계: 후처리 및 원하는 고전 형식으로 결과 반환하기

마지막 단계는 테스트된 각 Qubit 그룹에 대한 평균 제곱 오차(MSE) 메트릭을 계산하고 결과를 요약하는 것입니다. 각 체인에 대해 이제 측정된 Z0Z3\langle Z_0Z_3\rangleX0X3\langle X_0X_3\rangle이 있습니다. Qubit 0과 3이 Φ+|\Phi^+\rangle Bell 상태에서 완벽하게 얽혀 있다면, 이 두 값 모두 +1이 될 것으로 예상됩니다. MSE를 사용하여 편차를 정량화합니다.

MSE=(Z0Z31)2+(X0X31)22.\text{MSE} = \frac{( \langle Z_0Z_3\rangle - 1)^2 + (\langle X_0X_3\rangle - 1)^2}{2}.

이 값은 완벽한 Bell 쌍에 대해 0이며, 얽힘 상태가 노이즈가 많아질수록 증가합니다(임의의 결과가 기댓값 0에 가까워지면 MSE는 1에 가까워집니다). 코드는 각 4-Qubit 그룹에 대해 이 MSE를 계산합니다.

결과는 장치 전체에 걸쳐 얽힘 품질이 다양하게 분포되어 있음을 보여줍니다. 이는 사용되는 물리적 Qubit에 따라 Bell 상태 충실도가 한 자릿수 이상 차이가 날 수 있다는 논문의 발견을 확인합니다. 실용적인 관점에서, 이는 칩의 특정 영역이나 링크가 회로 중간 측정 및 피드포워드 연산에 훨씬 더 적합하다는 것을 의미합니다. Qubit 판독 오류, Qubit 수명, 크로스토크 등의 요인이 이러한 차이에 기여할 가능성이 높습니다. 예를 들어, 하나의 체인에 특히 노이즈가 많은 판독 Qubit이 포함되어 있다면, 회로 중간 측정이 신뢰할 수 없게 되어 해당 얽힘 쌍의 충실도가 낮아질 수 있습니다(높은 MSE).

layouts_mse = get_mse(job.result(), initial_layouts)
layout [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
qubits: [0, 1, 2, 3], mse:, 0.0312
qubits: [4, 5, 6, 7], mse:, 0.0491
qubits: [8, 9, 10, 11], mse:, 0.0711
qubits: [12, 13, 14, 15], mse:, 0.0436

layout [16, 23, 22, 21, 17, 27, 26, 25, 18, 31, 30, 29, 19, 35, 34, 33]
qubits: [16, 23, 22, 21], mse:, 0.0197
qubits: [17, 27, 26, 25], mse:, 0.113
qubits: [18, 31, 30, 29], mse:, 0.0287
qubits: [19, 35, 34, 33], mse:, 0.0433

layout [36, 41, 42, 43, 37, 45, 46, 47, 38, 49, 50, 51, 39, 53, 54, 55]
qubits: [36, 41, 42, 43], mse:, 0.1645
qubits: [37, 45, 46, 47], mse:, 0.0409
qubits: [38, 49, 50, 51], mse:, 0.0519
qubits: [39, 53, 54, 55], mse:, 0.0829

layout [56, 63, 62, 61, 57, 67, 66, 65, 58, 71, 70, 69, 59, 75, 74, 73]
qubits: [56, 63, 62, 61], mse:, 0.8663
qubits: [57, 67, 66, 65], mse:, 0.0375
qubits: [58, 71, 70, 69], mse:, 0.0664
qubits: [59, 75, 74, 73], mse:, 0.0291

layout [76, 81, 82, 83, 77, 85, 86, 87, 78, 89, 90, 91, 79, 93, 94, 95]
qubits: [76, 81, 82, 83], mse:, 0.0598
qubits: [77, 85, 86, 87], mse:, 0.313
qubits: [78, 89, 90, 91], mse:, 0.0679
qubits: [79, 93, 94, 95], mse:, 0.0505

layout [96, 103, 102, 101, 97, 107, 106, 105, 98, 111, 110, 109, 99, 115, 114, 113]
qubits: [96, 103, 102, 101], mse:, 0.0302
qubits: [97, 107, 106, 105], mse:, 0.0384
qubits: [98, 111, 110, 109], mse:, 0.0375
qubits: [99, 115, 114, 113], mse:, 0.1051

layout [116, 121, 122, 123, 117, 125, 126, 127, 118, 129, 130, 131, 119, 133, 134, 135]
qubits: [116, 121, 122, 123], mse:, 0.1624
qubits: [117, 125, 126, 127], mse:, 0.7246
qubits: [118, 129, 130, 131], mse:, 0.5919
qubits: [119, 133, 134, 135], mse:, 0.5277

layout [136, 143, 142, 141, 137, 147, 146, 145, 138, 151, 150, 149, 139, 155, 154, 153]
qubits: [136, 143, 142, 141], mse:, 0.0383
qubits: [137, 147, 146, 145], mse:, 1.0187
qubits: [138, 151, 150, 149], mse:, 0.1531
qubits: [139, 155, 154, 153], mse:, 0.0471

마지막으로, 모든 체인의 MSE 값에 대한 누적 분포 함수(CDF)를 플로팅하여 전반적인 성능을 시각화합니다. CDF 플롯은 x축에 MSE 임계값을, y축에 해당 MSE 이하인 Qubit 쌍의 비율을 표시합니다. 이 곡선은 0에서 시작하여 임계값이 커질수록 모든 데이터 포인트를 포함하게 됨에 따라 1에 가까워집니다. 낮은 MSE 근처에서 가파르게 상승한다면 많은 쌍이 높은 충실도를 가짐을 나타내고, 완만하게 상승한다면 많은 쌍이 더 큰 오차를 가짐을 의미합니다. CDF에는 가장 우수한 쌍의 식별 정보를 표시합니다. 플롯에서 CDF의 각 점은 하나의 4-Qubit 체인의 MSE에 해당하며, 해당 실험에서 얽혔던 Qubit 인덱스 쌍 [q0,q3][q0, q3]으로 레이블링됩니다. 이를 통해 어떤 물리적 Qubit 쌍이 최고 성능을 발휘하는지(CDF의 가장 왼쪽 점들) 쉽게 확인할 수 있습니다.

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

참고 문헌

[1] Carrera Vazquez, A., Tornow, C., Ristè, D. et al. Combining quantum processors with real-time classical communication. Nature 636, 75-79 (2024).