Dynamische Schaltkreise mit geschnittenen Bell-Paaren benchmarken
Geschätzter Verbrauch: 22 Sekunden auf einem Heron-r2-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine tatsächliche Laufzeit kann abweichen.)
Hintergrund
Quantenhardware ist typischerweise auf lokale Wechselwirkungen beschränkt, aber viele Algorithmen erfordern die Verschränkung weit entfernter Qubits oder sogar Qubits auf separaten Prozessoren. Dynamische Schaltkreise – also Schaltkreise mit Messungen mitten im Ablauf und Feedforward – bieten eine Möglichkeit, diese Einschränkungen zu überwinden, indem sie klassische Echtzeitkommunikation nutzen, um effektiv nicht-lokale Quantenoperationen zu implementieren. Bei diesem Ansatz können Messergebnisse aus einem Teil eines Schaltkreises (oder einem QPU) bedingt Gates auf einem anderen auslösen, was es uns ermöglicht, Verschränkung über große Distanzen zu teleportieren. Dies bildet die Grundlage von Local Operations and Classical Communication (LOCC)-Schemata, bei denen wir verschränkte Ressourcenzustände (Bell-Paare) verbrauchen und Messergebnisse klassisch kommunizieren, um entfernte Qubits zu verbinden.
Eine vielversprechende Anwendung von LOCC ist die Realisierung virtueller weitreichender CNOT-Gates durch Teleportation, wie im Tutorial zur Weitbereichsverschränkung gezeigt. Anstelle eines direkten weitreichenden CNOTs (den die Hardware-Konnektivität möglicherweise nicht erlaubt) erzeugen wir Bell-Paare und führen eine teleportationsbasierte Gate-Implementierung durch. Die Güte solcher Operationen hängt jedoch von den Hardware-Eigenschaften ab. Qubit-Dekohärenz während der notwendigen Verzögerung (beim Warten auf Messergebnisse) und die Latenz der klassischen Kommunikation können den verschränkten Zustand degradieren. Außerdem sind Fehler bei Messungen mitten im Schaltkreis schwerer zu korrigieren als Fehler bei abschließenden Messungen, da sie sich durch die bedingten Gates auf den Rest des Schaltkreises ausbreiten.
Im Referenzexperiment stellen die Autoren ein Bell-Paar-Güte-Benchmark vor, um zu identifizieren, welche Teile eines Geräts am besten für LOCC-basierte Verschränkung geeignet sind. Die Idee besteht darin, einen kleinen dynamischen Schaltkreis auf jeder Gruppe von vier verbundenen Qubits im Prozessor auszuführen. Dieser Vier-Qubit-Schaltkreis erzeugt zunächst ein Bell-Paar auf zwei mittleren Qubits und nutzt diese dann als Ressource, um die zwei äußeren Qubits per LOCC zu verschränken. Konkret werden Qubit 1 und 2 lokal in ein ungeschnittenes Bell-Paar gebracht (mit einem Hadamard und CNOT), und dann verbraucht eine Teleportationsroutine dieses Bell-Paar, um Qubit 0 und 3 zu verschränken. Qubits 1 und 2 werden während der Ausführung des Schaltkreises gemessen, und basierend auf diesen Ergebnissen werden Pauli-Korrekturen angewandt (ein X auf Qubit 3 und Z auf Qubit 0). Qubits 0 und 3 befinden sich am Ende des Schaltkreises in einem Bell-Zustand.
Um die Qualität dieses finalen verschränkten Paares zu quantifizieren, messen wir seine Stabilisatoren: konkret die Parität in der -Basis () und in der -Basis (). Für ein perfektes Bell-Paar sind beide Erwartungswerte gleich +1. In der Praxis werden diese Werte durch Hardware-Rauschen reduziert. Wir wiederholen den Schaltkreis daher zweimal für jedes Qubit-Paar: ein Schaltkreis misst Qubits 0 und 3 in der -Basis, ein anderer in der -Basis. Aus den Ergebnissen erhalten wir eine Schätzung von und für das jeweilige Qubit-Paar. Wir verwenden den mittleren quadratischen Fehler (MSE) dieser Stabilisatoren gegenüber dem Idealwert (1) als einfaches Maß für die Verschränkungsgüte. Ein niedrigerer MSE bedeutet, dass die zwei Qubits einen Bell-Zustand näher am Ideal erreicht haben (höhere Güte), während ein höherer MSE mehr Fehler anzeigt. Indem wir dieses Experiment über das gesamte Gerät durchführen, können wir die Mess-und-Feedforward-Fähigkeit verschiedener Qubit-Gruppen benchmarken und die besten Qubit-Paare für LOCC-Operationen identifizieren.
Dieses Tutorial demonstriert das Experiment auf einem IBM Quantum®-Gerät, um zu veranschaulichen, wie dynamische Schaltkreise zur Erzeugung und Bewertung von Verschränkung zwischen entfernten Qubits eingesetzt werden können. Wir werden alle vier Qubit umfassenden linearen Ketten auf dem Gerät kartieren, den Teleportationsschaltkreis auf jeder davon ausführen und dann die Verteilung der MSE-Werte visualisieren. Dieses End-to-End-Verfahren zeigt, wie Qiskit Runtime und dynamische Schaltkreis-Funktionen genutzt werden können, um hardware-bewusste Entscheidungen beim Aufteilen von Schaltkreisen oder der Verteilung von Quantenalgorithmen über ein modulares System zu treffen.
Voraussetzungen
Bevor du mit diesem Tutorial beginnst, stelle sicher, dass du Folgendes installiert hast:
- Qiskit SDK v2.0 oder höher, mit Unterstützung für Visualisierung
- Qiskit Runtime v0.40 oder höher (
pip install qiskit-ibm-runtime)
Einrichtung
# 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()
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
Der erste Schritt besteht darin, eine Menge von Quantenschaltkreisen zu erstellen, um alle Kandidaten-Bell-Paar-Verbindungen passend zur Topologie des Geräts zu benchmarken. Wir durchsuchen die Kopplungskarte des Geräts programmatisch nach allen linear verbundenen Ketten aus vier Qubits. Jede solche Kette (bezeichnet durch Qubit-Indizes ) dient als Testfall für den Verschränkungs-Swap-Schaltkreis. Durch die Identifizierung aller möglichen Pfade der Länge 4 stellen wir eine maximale Abdeckung für mögliche Qubit-Gruppierungen sicher, die das Protokoll realisieren könnten.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
Wir generieren diese Ketten mithilfe einer Hilfsfunktion, die eine gierige Suche auf dem Gerätegraph durchführt. Sie gibt „Stripes" zurück – Gruppen von vier Vier-Qubit-Ketten, die zu 16-Qubit-Gruppen gebündelt sind (dynamische Schaltkreise beschränken die Größe des Messregisters derzeit auf 16 Qubits). Die Bündelung ermöglicht es uns, mehrere Vier-Qubit-Experimente parallel auf verschiedenen Teilen des Chips auszuführen und das gesamte Gerät effizient zu nutzen. Jeder 16-Qubit-Stripe enthält vier disjunkte Ketten, das heißt, kein Qubit wird innerhalb einer Gruppe wiederverwendet. Ein Stripe könnte beispielsweise aus den Ketten , , und bestehen, alle zusammengefasst. Qubits, die in keinen Stripe aufgenommen wurden, werden in der Variable leftover zurückgegeben.
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)
Als nächstes konstruieren wir den Schaltkreis für jeden 16-Qubit-Stripe. Die Routine führt für jede Kette Folgendes durch:
- Ein mittleres Bell-Paar vorbereiten: Einen Hadamard auf Qubit 1 und einen CNOT von Qubit 1 nach Qubit 2 anwenden. Dies verschränkt Qubits 1 und 2 (und erzeugt den Bell-Zustand ).
- Die äußeren Qubits verschränken: Einen CNOT von Qubit 0 nach Qubit 1 und einen CNOT von Qubit 2 nach Qubit 3 anwenden. Dadurch werden die zunächst getrennten Paare verbunden, sodass Qubits 0 und 3 nach den nächsten Schritten verschränkt sind. Außerdem wird ein Hadamard auf Qubit 2 angewandt (dieser bildet zusammen mit den vorherigen CNOTs einen Teil der Bell-Messung auf Qubits 1 und 2). Zu diesem Zeitpunkt sind Qubits 0 und 3 noch nicht verschränkt, aber Qubits 1 und 2 sind mit ihnen in einem größeren Vier-Qubit-Zustand verschränkt.
- Messungen mitten im Schaltkreis und Feedforward: Qubits 1 und 2 (die mittleren Qubits) werden in der Rechenbasis gemessen, was zwei klassische Bits ergibt. Basierend auf diesen Messergebnissen werden bedingte Operationen angewandt: Wenn die Messung von Qubit 1 (Bit ) den Wert 1 hat, wird ein -Gate auf Qubit 3 angewandt; wenn die Messung von Qubit 2 () den Wert 1 hat, wird ein -Gate auf Qubit 0 angewandt. Diese bedingten Gates (realisiert mit dem Qiskit-Konstrukt
if_test/if_else) implementieren die Standard-Teleportationskorrekturen. Sie „machen" die zufälligen Pauli-Flips rückgängig, die durch die Projektion der Qubits 1 und 2 entstehen, und stellen sicher, dass Qubits 0 und 3 unabhängig von den Messergebnissen in einem bekannten Bell-Zustand enden. Nach diesem Schritt sollten Qubits 0 und 3 idealerweise im Bell-Zustand verschränkt sein. - Bell-Paar-Stabilisatoren messen: Wir teilen den Schaltkreis dann in zwei Versionen auf. In der ersten Version messen wir den -Stabilisator auf Qubits 0 und 3. In der zweiten Version messen wir den -Stabilisator auf diesen Qubits.
Für jedes Vier-Qubit-Initial-Layout gibt die obige Funktion zwei Schaltkreise zurück (einen für den -, einen für den -Stabilisatormessung). Am Ende dieses Schritts haben wir eine Liste von Schaltkreisen, die jede Vier-Qubit-Kette auf dem Gerät abdecken. Diese Schaltkreise enthalten Messungen mitten im Ablauf und bedingte (if/else)-Operationen – die zentralen Anweisungen des dynamischen Schaltkreises.
circuits = create_bell_stab(initial_layouts)
circuits[-1].draw("mpl", fold=-1)

Schritt 2: Das Problem für die Quantenhardware-Ausführung optimieren
Bevor wir unsere Schaltkreise auf echter Hardware ausführen, müssen wir sie transpilieren, um sie an die physikalischen Einschränkungen des Geräts anzupassen. Die Transpilierung bildet den abstrakten Schaltkreis auf die physikalischen Qubits und den Gate-Satz des gewählten Geräts ab. Da wir bereits spezifische physikalische Qubits für jede Kette gewählt haben (durch Angabe eines initial_layout im Schaltkreis-Generator), verwenden wir optimization_level=0 im Transpiler mit diesem festen Layout. Dies weist Qiskit an, die Qubits nicht neu zuzuordnen oder schwere Optimierungen durchzuführen, die die Schaltkreisstruktur verändern könnten. Wir möchten die Abfolge der Operationen (insbesondere die bedingten Gates) genau wie angegeben beibehalten.
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)

Schritt 3: Ausführung mit Qiskit-Primitiven
Wir können das Experiment jetzt auf dem Quantengerät ausführen. Wir verwenden Qiskit Runtime und sein Sampler-Primitive, um den Batch von Schaltkreisen effizient auszuführen.
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["cut-bell-pair-test"]
job = sampler.run(isa_circuits)
Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
Der letzte Schritt besteht darin, die MSE-Metrik (mittlerer quadratischer Fehler) für jede getestete Qubit-Gruppe zu berechnen und die Ergebnisse zusammenzufassen. Für jede Kette haben wir jetzt die gemessenen und . Wenn Qubits 0 und 3 perfekt in einem -Bell-Zustand verschränkt wären, würden wir erwarten, dass beide den Wert +1 haben. Wir quantifizieren die Abweichung mit dem MSE:
Dieser Wert ist 0 für ein perfektes Bell-Paar und steigt mit zunehmendem Rauschen des verschränkten Zustands (bei zufälligen Ergebnissen mit einem Erwartungswert um 0 würde der MSE gegen 1 gehen). Der Code berechnet diesen MSE für jede Vier-Qubit-Gruppe.
Die Ergebnisse zeigen eine große Bandbreite an Verschränkungsqualität über das gesamte Gerät. Dies bestätigt den Befund der Veröffentlichung, dass die Bell-Zustand-Güte je nach verwendeten physikalischen Qubits um mehr als eine Größenordnung variieren kann. In der Praxis bedeutet dies, dass bestimmte Regionen oder Verbindungen im Chip deutlich besser für Messungen mitten im Schaltkreis und Feedforward-Operationen geeignet sind als andere. Faktoren wie Qubit-Auslesefehler, Qubit-Lebensdauer und Übersprechen tragen wahrscheinlich zu diesen Unterschieden bei. Enthält eine Kette beispielsweise ein besonders verrauschtes Ausle-Qubit, könnte die Messung mitten im Schaltkreis unzuverlässig sein, was zu einer schlechten Güte des verschränkten Paares führt (hoher 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
Abschließend visualisieren wir die Gesamtleistung, indem wir die kumulative Verteilungsfunktion (CDF) der MSE-Werte für alle Ketten darstellen. Der CDF-Plot zeigt den MSE-Schwellenwert auf der x-Achse und den Anteil der Qubit-Paare, die höchstens diesen MSE haben, auf der y-Achse. Diese Kurve beginnt bei null und nähert sich eins an, wenn der Schwellenwert alle Datenpunkte umfasst. Ein steiler Anstieg bei einem niedrigen MSE würde darauf hindeuten, dass viele Paare eine hohe Güte haben; ein langsamer Anstieg bedeutet, dass viele Paare größere Fehler aufweisen. Wir annotieren die CDF mit den Identitäten der besten Paare. Im Plot entspricht jeder Punkt der CDF dem MSE einer Vier-Qubit-Kette, und wir beschriften den Punkt mit dem Paar von Qubit-Indizes , die in diesem Experiment verschränkt wurden. Dies macht es einfach, die leistungsstärksten physikalischen Qubit-Paare zu erkennen (die am weitesten links liegenden Punkte der CDF).
plot_mse_ecdfs(layouts_mse, combine_layouts=True)