Přeskočit na hlavní obsah

Benchmark dynamických obvodů s přerušenými Bellovými páry

Odhadovaná spotřeba: 22 sekund na procesoru Heron r2 (POZNÁMKA: Jde pouze o odhad. Skutečný čas může být jiný.)

Pozadí

Kvantový hardware je typicky omezen na lokální interakce, ale mnoho algoritmů vyžaduje propletení vzdálených Qubitů nebo dokonce Qubitů na oddělených procesorech. Dynamické obvody – tedy obvody s měřením uprostřed obvodu a zpětnou vazbou – nabízejí způsob, jak tato omezení překonat, a to pomocí komunikace klasickými kanály v reálném čase, která efektivně implementuje nelokalní kvantové operace. V tomto přístupu mohou výsledky měření z jedné části obvodu (nebo jedné QPU) podmíněně spouštět brány v jiné části, což nám umožňuje teleportovat propletení na velké vzdálenosti. To tvoří základ schémat lokálních operací a klasické komunikace (LOCC), kde spotřebováváme propletené zdroje (Bellovy páry) a klasicky předáváme výsledky měření, abychom propojili vzdálené Qubity.

Jedním ze slibných využití LOCC je realizace virtuálních dalekosáhlých CNOT bran pomocí teleportace, jak je ukázáno v tutoriálu o dalekosáhlém propletení. Místo přímé dalekosáhlé CNOT (kterou hardwarová konektivita nemusí umožňovat) vytvoříme Bellovy páry a provedeme implementaci brány založenou na teleportaci. Věrnost takových operací však závisí na vlastnostech hardwaru. Dekoherence Qubitů během nutné prodlevy (při čekání na výsledky měření) a latence klasické komunikace mohou degradovat propletený stav. Chyby při měřeních uprostřed obvodu jsou navíc obtížněji opravitelné než chyby při závěrečných měřeních, protože se skrze podmíněné brány šíří do zbytku obvodu.

V referenčním experimentu autoři představují benchmark věrnosti Bellových párů, který identifikuje, které části zařízení jsou nejlépe vhodné pro propletení pomocí LOCC. Základní myšlenkou je spustit malý dynamický obvod na každé skupině čtyř propojených Qubitů v procesoru. Tento čtyř-qubitový obvod nejprve vytvoří Bellův pár na dvou prostředních Qubitech, které pak použije jako zdroj k propletení dvou okrajových Qubitů pomocí LOCC. Konkrétně jsou Qubity 1 a 2 lokálně připraveny do nepřerušeného Bellova páru (pomocí Hadamardovy brány a CNOT) a poté teleportační rutina spotřebuje tento Bellův pár k propletení Qubitů 0 a 3. Qubity 1 a 2 jsou měřeny během provádění obvodu a na základě výsledků jsou aplikovány Pauliho korekce (X na Qubit 3 a Z na Qubit 0). Qubity 0 a 3 jsou pak na konci obvodu v Bellově stavu.

Abychom kvantifikovali kvalitu tohoto výsledného propletého páru, měříme jeho stabilizátory: konkrétně paritu v bázi ZZ (Z0Z3Z_0Z_3) a v bázi XX (X0X3X_0X_3). Pro dokonalý Bellův pár jsou obě tyto střední hodnoty rovny +1. V praxi je hluk hardwaru sníží. Proto spouštíme obvod dvakrát pro každý pár Qubitů: jeden obvod měří Qubity 0 a 3 v bázi ZZ a druhý v bázi XX. Z výsledků získáme odhad Z0Z3\langle Z_0Z_3\rangle a X0X3\langle X_0X_3\rangle pro daný pár Qubitů. Jako jednoduchou metriku věrnosti propletení používáme střední kvadratickou chybu (MSE) těchto stabilizátorů vůči ideální hodnotě (1). Nižší MSE znamená, že oba Qubity dosáhly Bellova stavu bližšího ideálu (vyšší věrnost), zatímco vyšší MSE naznačuje více chyb. Prohledáním tohoto experimentu přes celé zařízení můžeme benchmarkovat schopnost měření a zpětné vazby různých skupin Qubitů a identifikovat nejlepší páry Qubitů pro operace LOCC.

Tento tutoriál demonstruje experiment na zařízení IBM Quantum®, aby ilustroval, jak lze dynamické obvody použít ke generování a vyhodnocení propletení mezi vzdálenými Qubity. Zmapujeme všechny čtyřqubitové lineární řetězce na zařízení, spustíme teleportační obvod na každém z nich a poté vizualizujeme rozdělení hodnot MSE. Tento komplexní postup ukazuje, jak využít Qiskit Runtime a funkce dynamických obvodů k informovaným rozhodnutím o řezání obvodů nebo distribuci kvantových algoritmů přes modulární systém.

Požadavky

Než začneš s tímto tutoriálem, ujisti se, že máš nainstalováno:

  • Qiskit SDK v2.0 nebo novější, s podporou vizualizace
  • Qiskit Runtime v0.40 nebo novější (pip install qiskit-ibm-runtime)

Nastavení

# 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()

Krok 1: Převod klasických vstupů na kvantový problém

Prvním krokem je vytvořit sadu kvantových Circuit pro benchmarking všech kandidátních Bell-pair spojení přizpůsobených topologii zařízení. Programově prohledáme coupling mapu zařízení a najdeme všechny lineárně propojené řetězce čtyř Qubitů. Každý takový řetězec (označený indexy Qubitů [q0q1q2q3][q0-q1-q2-q3]) slouží jako testovací případ pro Circuit s výměnou entanglementu. Identifikací všech možných cest délky 4 zajistíme maximální pokrytí možných skupin Qubitů, které by mohly realizovat protokol.

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

Tyto řetězce generujeme pomocí pomocné funkce, která provádí hladové prohledávání grafu zařízení. Vrací „pruhy" čtyř čtyř-qubitových řetězců sdružených do 16-qubitových skupin (dynamické Circuit momentálně omezují velikost měřicího registru na 16 Qubitů). Sdružování nám umožňuje spouštět více čtyř-qubitových experimentů paralelně na různých částech čipu a efektivně využívat celé zařízení. Každý 16-qubitový pruh obsahuje čtyři disjunktní řetězce, což znamená, že žádný Qubit není v dané skupině použit vícekrát. Například jeden pruh může sestávat z řetězců [0123][0-1-2-3], [4567][4-5-6-7], [891011][8-9-10-11] a [12131415][12-13-14-15] zabalených dohromady. Jakýkoli Qubit, který nebyl zahrnut do pruhu, je vrácen v proměnné 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)

Dále sestavíme Circuit pro každý 16-qubitový pruh. Rutina provede pro každý řetězec následující kroky:

  • Připraví středový Bell pair: Aplikuje Hadamardovu Gate na Qubit 1 a CNOT z Qubitu 1 na Qubit 2. Tím se Qubity 1 a 2 zapletou (vznikne Bellův stav Φ+=(00+11)/2|\Phi^+\rangle = (|00\rangle + |11\rangle)/\sqrt{2}).
  • Zaplete krajní Qubity: Aplikuje CNOT z Qubitu 0 na Qubit 1 a CNOT z Qubitu 2 na Qubit 3. Tím se propojí původně oddělené páry tak, aby se Qubity 0 a 3 po dalších krocích zapletly. Aplikuje se také Hadamardova Gate na Qubit 2 (ta spolu s předchozími CNOTy tvoří část Bellova měření na Qubitech 1 a 2). V tomto bodě Qubity 0 a 3 ještě nejsou zapleteny, ale Qubity 1 a 2 jsou s nimi provázány ve větším čtyř-qubitovém stavu.
  • Mid-circuit měření a feedforward: Qubity 1 a 2 (střední Qubity) jsou měřeny v výpočetní bázi, čímž vzniknou dva klasické bity. Na základě výsledků měření aplikujeme podmíněné operace: pokud je měření Qubitu 1 (říkejme tomuto bitu m12m_{12}) rovno 1, aplikujeme Gate XX na Qubit 3; pokud je měření Qubitu 2 (m21m_{21}) rovno 1, aplikujeme Gate ZZ na Qubit 0. Tyto podmíněné Gate (realizované pomocí konstruktu Qiskit if_test/if_else) implementují standardní korekce teleportace. „Odstraní" náhodné Pauliho překlopy, ke kterým dochází při projekci Qubitů 1 a 2, a zajistí, že Qubity 0 a 3 skončí ve známém Bellově stavu bez ohledu na výsledky měření. Po tomto kroku by Qubity 0 a 3 měly být ideálně zapleteny v Bellově stavu Φ+|\Phi^+\rangle.
  • Měření stabilizátorů Bell pair: Poté Circuit rozdělíme na dvě verze. V první verzi měříme stabilizátor ZZZZ na Qubitech 0 a 3. Ve druhé verzi měříme stabilizátor XXXX na těchto Qubitech.

Pro každé čtyř-qubitové počáteční rozmístění výše uvedená funkce vrátí dva Circuit (jeden pro měření stabilizátoru ZZZZ, jeden pro XXXX). Na konci tohoto kroku máme seznam Circuit pokrývajících každý čtyř-qubitový řetězec na zařízení. Tyto Circuit zahrnují mid-circuit měření a podmíněné (if/else) operace, které jsou klíčovými instrukcemi dynamického Circuit.

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

Output of the previous code cell

Krok 2: Optimalizace problému pro spuštění na kvantovém hardware

Před spuštěním našich Circuit na skutečném hardware je musíme transpilovat tak, aby odpovídaly fyzickým omezením zařízení. Transpilace namapuje abstraktní Circuit na fyzické Qubity a sadu Gate zvoleného zařízení. Protože jsme pro každý řetězec již zvolili konkrétní fyzické Qubity (zadáním initial_layout do generátoru Circuit), použijeme Transpiler s optimization_level=0 s tímto pevným rozmístěním. Tím říkáme Qiskitu, aby nepřeřazoval Qubity ani neprováděl žádné náročné optimalizace, které by mohly změnit strukturu Circuit. Chceme zachovat posloupnost operací (zejména podmíněné Gate) přesně tak, jak je specifikována.

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

Krok 3: Spuštění pomocí primitiv Qiskit

Nyní můžeme experiment spustit na kvantovém zařízení. Ke efektivnímu spuštění dávky Circuit použijeme Qiskit Runtime a jeho primitiv Sampler.

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

Krok 4: Následné zpracování a vrácení výsledku v požadovaném klasickém formátu

Posledním krokem je vypočítat metriku střední kvadratické chyby (MSE) pro každou testovanou skupinu Qubitů a shrnout výsledky. Pro každý řetězec nyní máme naměřené hodnoty Z0Z3\langle Z_0Z_3\rangle a X0X3\langle X_0X_3\rangle. Pokud by byly Qubity 0 a 3 dokonale zapleteny v Bellově stavu Φ+|\Phi^+\rangle, očekávali bychom, že obě tyto hodnoty budou +1. Odchylku kvantifikujeme pomocí MSE:

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

Tato hodnota je 0 pro dokonalý Bell pair a roste s tím, jak je zapletený stav šumnější (při náhodných výsledcích, kdy je střední hodnota kolem 0, by se MSE blížilo 1). Kód vypočítá tuto MSE pro každou čtyř-qubitovou skupinu.

Výsledky odhalují velké rozmezí kvality entanglementu napříč zařízením. To potvrzuje závěr práce, že fidelita Bellova stavu může záviset na použitých fyzických Qubitech s rozdíly přesahujícími řád velikosti. V praxi to znamená, že určité oblasti nebo spoje v čipu jsou při provádění mid-circuit měření a feedforward operací výrazně lepší než jiné. K těmto rozdílům pravděpodobně přispívají faktory jako chyba čtení Qubitu, doba života Qubitu a přeslechy. Například pokud jeden řetězec obsahuje zvláště hlučný čtecí Qubit, mid-circuit měření může být nespolehlivé, což povede k nízké fidelitě daného zapleteneho páru (vysoká 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

Nakonec vizualizujeme celkový výkon vykreslením kumulativní distribuční funkce (CDF) hodnot MSE pro všechny řetězce. Graf CDF ukazuje na ose x práh MSE a na ose y podíl párů Qubitů, jejichž MSE je nejvýše dané hodnotě. Tato křivka začíná na nule a blíží se jedné, jak práh roste tak, aby zahrnoval všechny datové body. Strmý nárůst poblíž nízké MSE by znamenal, že mnoho párů má vysokou fidelitu; pomalý nárůst znamená, že mnoho párů má větší chyby. CDF doplníme identitami nejlepších párů. V grafu každý bod CDF odpovídá MSE jednoho čtyř-qubitového řetězce a označíme ho indexy páru Qubitů [q0,q3][q0, q3], které byly v daném experimentu zapleteny. Díky tomu je snadné rozpoznat, které fyzické páry Qubitů jsou nejlepšími výkonnými hráči (body zcela vlevo na CDF).

plot_mse_ecdfs(layouts_mse, combine_layouts=True)

Output of the previous code cell

Reference

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