Benchmark-Dynamikschaltungen mit abgeschnittenen Bell-Paaren
Geschätzte Nutzungsdauer: 22 Sekunden auf einem r2 Heron-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Ihre Laufzeit kann variieren.)
Lernziele
- Wie man dynamische Schaltungen mit Messungen während des Schaltvorgangs und klassischer Vorwärtskopplung baut, um Verschränkungen zwischen weit voneinander entfernten Qubits zu teleportieren;
- Wie man die Fehlermetrik berechnet und interpretiert, um die Bell-Paar-Fidelity auf einem Gerät zu quantifizieren;
- Wie lässt sich anhand der Benchmark-Ergebnisse feststellen, welche Qubit-Paare für LOCC-basierte Operationen am besten geeignet sind?
Voraussetzungen
- Grundlegende Konzepte der Quanteninformatik, darunter Bell-Zustände, Verschränkung und Quantenlogikgatter;
- Kenntnisse im Bereich dynamischer Schaltungen ( Messungen während des Betriebs und klassische Vorwärtsregelung);
- Grundkenntnisse in „ Qiskit SDK “ und „ IBM Quantum® Compute Service“ sowie Zugriff auf ein „ IBM Quantum “-Konto.
Hintergrund
Quantenhardware ist in der Regel auf lokale Interaktionen beschränkt, aber viele Algorithmen erfordern die Verschränkung entfernter Qubits oder sogar Qubits auf separaten Prozessoren. Dynamische Schaltungen – also Schaltungen mit Messung in der Mitte der Schaltung und Vorwärtskopplung – bieten eine Möglichkeit, diese Einschränkungen zu überwinden, indem sie klassische Echtzeitkommunikation nutzen, um nicht-lokale Quantenoperationen effektiv umzusetzen. Bei diesem Ansatz können die Messergebnisse aus einem Teil eines Schaltkreises (oder einer QPU) unter bestimmten Bedingungen Gates in einem anderen Teil auslösen, wodurch wir Verschränkungen über große Entfernungen teleportieren können. Dies bildet die Grundlage für lokale Operationen und klassische Kommunikationsschemata (LOCC), bei denen wir verschränkte Ressourcenzustände (Bell-Paare) verbrauchen und Messergebnisse klassisch kommunizieren, um entfernte Qubits miteinander zu verbinden.
Eine vielversprechende Anwendung von LOCC ist die Realisierung virtueller CNOT-Gatter mit großer Reichweite durch Teleportation, wie im Tutorial zur Verschränkung mit großer Reichweite gezeigt wird. Anstelle eines direkten Langstrecken-CNOT (was aufgrund der Hardware-Konnektivität möglicherweise nicht zulässig ist) erstellen wir Bell-Paare und führen eine teleportationsbasierte Gate-Implementierung durch. Die Genauigkeit solcher Vorgänge hängt jedoch von den Hardwareeigenschaften ab. Die Dekohärenz von Qubits während der erforderlichen Verzögerung (während des Wartens auf die Messergebnisse) und die Latenz der klassischen Kommunikation können den verschränkten Zustand beeinträchtigen. Außerdem sind Fehler bei Messungen während des Schaltkreislaufs schwieriger zu korrigieren als Fehler bei Endmessungen, da sie sich über die bedingten Gatter auf den Rest des Schaltkreises ausbreiten.
Im Refer enzversuch führen die Autoren einen Maßstab für die Treue eines Bell-Paares ein, um zu ermitteln, welche Teile eines Geräts sich am besten für LOCC-basierte Verschränkung eignen. Die Idee besteht darin, auf jeder Gruppe von vier miteinander verbundenen Qubits im Prozessor eine kleine dynamische Schaltung auszuführen. Dieser Vier-Qubit-Schaltkreis erzeugt zunächst ein Bell-Paar auf den beiden mittleren Qubits und nutzt diese dann als Ressource, um die beiden Rand-Qubits mithilfe von LOCC zu verschränken. Konkret werden die Qubits 1 und 2 lokal in einem ungeschnittenen Bell-Paar (einem Bell-Paar, das direkt mit einem Hadamard- und einem CNOT-Operator ohne Teleportation erzeugt wurde) vorbereitet, und anschließend verbraucht eine Teleportationsroutine dieses Bell-Paar, um die Qubits 0 und 3 zu verschränken. Die Qubits 1 und 2 werden während der Ausführung der Schaltung gemessen, und auf Grundlage dieser Ergebnisse werden Pauli-Korrekturen (ein X auf Qubit 3 und ein Z auf Qubit 0) vorgenommen. Die Qubits 0 und 3 befinden sich am Ende des Schaltkreises dann in einem Bell-Zustand.
Um die Qualität dieses endgültigen verschränkten Paares zu quantifizieren, messen wir seine Stabilisatoren: insbesondere die Parität in der Basis ( ) und in der Basis ( ). Bei einem perfekten Bell-Paar sind beide Erwartungen gleich +1. In der Praxis werden diese Werte durch Hardware-Rauschen verringert. Wir wiederholen daher den Schaltkreis zweimal für jedes Qubit-Paar: Ein Schaltkreis misst die Qubits 0 und 3 in der Basis, ein anderer misst sie in der Basis. Aus den Ergebnissen erhalten wir eine Schätzung von und für dieses Qubit-Paar. Wir verwenden den mittleren quadratischen Fehler (MSE) dieser Stabilisatoren in Bezug auf den Idealwert (1) als einfache Metrik für die Verschränkungsgenauigkeit. Ein niedrigerer MSE-Wert bedeutet, dass die beiden Qubits einen Bell-Zustand erreicht haben, der näher am Ideal liegt (höhere Genauigkeit), während ein höherer MSE-Wert auf mehr Fehler hinweist. Durch das Scannen dieses Experiments über das gesamte Gerät können wir die Mess- und Feedforward-Fähigkeiten verschiedener Qubit-Gruppen benchmarken und die besten Qubit-Paare für LOCC-Operationen identifizieren.
Dieses Tutorial zeigt anhand eines Experiments mit einem „ IBM Quantum® “-Gerät, wie dynamische Schaltungen genutzt werden können, um Verschränkung zwischen weit voneinander entfernten Qubits zu erzeugen und zu bewerten. Wir werden alle linearen Ketten mit vier Qubits auf dem Gerät abbilden, auf jeder davon den Teleportationsschaltkreis ausführen und anschließend die Verteilung der MSE-Werte visualisieren. Dieses durchgängige Verfahren zeigt, wie man den „ IBM Quantum “ Compute Service und dynamische Schaltkreisfunktionen nutzen kann, um hardwareorientierte Entscheidungen beim Entwurf von Schaltkreisen oder bei der Verteilung von Quantenalgorithmen über ein modulares System zu treffen.
Anforderungen
Bevor Sie mit diesem Tutorial beginnen, stellen Sie sicher, dass Sie Folgendes installiert haben:
- Qiskit SDK v2.0 oder später, mit Visual isierungsunterstützung
- v0.40Qiskit Runtime oder später (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 oder höher (
pip install qiskit-aer)
Einrichtung
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
), "The length of the chain must be a multiple of 4, "
f"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:, "
f"{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()Beispiel für einen Simulator im kleinen Maßstab
Bevor wir den Schaltkreis auf der echten QPU ausführen, überprüfen wir, ob er ein Bell-Paar erzeugt, indem wir ihn auf einem rauschfreien Simulator mit einer Vier-Qubit-Kette testen [0, 1, 2, 3]. Wir verwenden den „ IBM Quantum “ Sampler mit dem Backend-Modus AerSimulator „as“, um die Schaltkreise auszuführen.
Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen
Der erste Schritt besteht darin, eine Reihe von Quantenschaltungen zu erstellen, um alle in Frage kommenden Bell-Paar-Verbindungen zu testen, die auf die Topologie des Geräts zugeschnitten sind. [0, 1, 2, 3]Wir beginnen damit, solche Schaltungen mit einer Vier-Qubit-Kette zu konstruieren.
Die create_bell_stab() Routine führt für jede Kette Folgendes aus:
- Bereiten Sie ein mittleres Bell-Paar vor: Wenden Sie einen Hadamard-Operator auf Qubit 1 und einen CNOT-Operator von Qubit 1 auf Qubit 2 an. Dadurch werden die Qubits 1 und 2 verschränkt (wodurch ein Bell-Zustand entsteht).
- Verflechten Sie die Rand-Qubits: Wenden Sie ein CNOT von Qubit 0 auf Qubit 1 und ein CNOT von Qubit 2 auf Qubit 3 an. Dadurch werden die ursprünglich getrennten Paare miteinander verbunden, sodass die Qubits 0 und 3 nach den nächsten Schritten verschränkt werden. Ein Hadamard auf Qubit 2 wird ebenfalls angewendet (dies bildet zusammen mit den vorherigen CNOTs einen Teil einer Bell-Messung auf Qubits 1 und 2). Zu diesem Zeitpunkt sind die Qubits 0 und 3 noch nicht verschränkt, aber die Qubits 1 und 2 sind mit ihnen in einem größeren Vier-Qubit-Zustand verschränkt.
- Messungen während des Zyklus und Vorwärtskopplung: Die Qubits 1 und 2 (die mittleren Qubits) werden in der Rechenbasis gemessen, was zwei klassische Bits ergibt. Basierend auf diesen Messergebnissen wenden wir bedingte Operationen an: Wenn die Messung des Qubits 1 (nennen wir dieses Bit ) 1 ist, wenden wir ein Gatter auf Qubit 3 an; wenn die Messung des Qubits 2 ( ) 1 ist, wenden wir ein Gatter auf Qubit 0 an. Diese bedingten Gatter (realisiert unter Verwendung des
if_else``if_testQiskit-/Konstrukts) implementieren die Standard-Teleportationskorrekturen. Sie „heben“ die zufälligen Pauli-Flips auf, die aufgrund der Projektion der Qubits 1 und 2 auftreten, und stellen so sicher, dass die Qubits 0 und 3 unabhängig von den Messergebnissen in einem bekannten Bell-Zustand enden. Nach diesem Schritt sollten die Qubits 0 und 3 idealerweise im Bell-Zustand verschränkt sein. - Messung der Glockenpaar-Stabilisatoren: Anschließend haben wir die Schaltung in zwei Versionen aufgeteilt. In der ersten Version messen wir den Stabilisator auf den Qubits 0 und 3. In der zweiten Version messen wir den Stabilisator auf diesen Qubits.
Für jede anfängliche Anordnung mit vier Qubits gibt die obige Funktion zwei Schaltkreise zurück (einen für die Stabilisatormessung „ “ und einen für die Stabilisatormessung „ “). Diese Schaltungen umfassen Messungen während der Ausführung sowie bedingte (if/else-)Operationen, die die zentralen Befehle der dynamischen Schaltung darstellen.
from qiskit_aer import AerSimulator
# 4-qubit chain for simulation
sim_layout = [[0, 1, 2, 3]]
aer_backend = AerSimulator()
sim_circuits = create_bell_stab(sim_layout)
sim_circuits[1].draw("mpl", fold=-1, idle_wires=False)Output:
Schritt 2: Optimieren Sie das Problem für die Ausführung auf Quantenhardware
Bevor wir unsere Schaltkreise ausführen, müssen wir sie in Gate-Operationen transpilieren, die vom angegebenen Backend unterstützt werden. Bei der Transpilation wird die abstrakte Schaltung auf die physikalischen Qubits und den Gatter-Satz des ausgewählten Backends abgebildet. Da wir bereits für jede Kette bestimmte physikalische Qubits ausgewählt haben (indem wir dem Schaltkreisgenerator einen initial_layout übergeben haben), verwenden wir den Transpiler optimization_level=0 mit diesem festen Layout. Dadurch wird Qiskit angewiesen, keine Qubits neu zuzuweisen oder umfangreiche Optimierungen durchzuführen, die die Schaltungsstruktur verändern könnten. Wir möchten die Abfolge der Operationen (insbesondere die bedingten Verknüpfungen) genau so beibehalten, wie sie angegeben ist.
pm_sim = generate_preset_pass_manager(
optimization_level=0, backend=aer_backend, initial_layout=sim_layout[0]
)
isa_sim_circuits = pm_sim.run(sim_circuits)
isa_sim_circuits[1].draw("mpl", fold=-1, idle_wires=False)Schritt 3: Mit „ Qiskit primitives “ ausführen
Wir können das Experiment nun auf dem rauschfreien Simulator-Backend ausführen.
# Run on noiseless simulator
sampler_sim = Sampler(mode=aer_backend)
sim_job = sampler_sim.run(isa_sim_circuits)
sim_mse = get_mse(sim_job.result(), sim_layout)Output:
layout [0, 1, 2, 3]
qubits: [0, 1, 2, 3], mse:, 0.0
Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
Der letzte Schritt besteht darin, die mittlere quadratische Fehlermetrik (MSE) für jede getestete Qubit-Gruppe zu berechnen und die Ergebnisse zusammenzufassen. Für jede Kette haben wir nun die gemessenen Werte und . Wenn die Qubits 0 und 3 in einem Bell-Zustand perfekt verschränkt wären, würden wir erwarten, dass beide Werte +1 sind. Wir quantifizieren die Abweichung mithilfe der MSE:
Dieser Wert beträgt 0 für ein perfektes Bell-Paar und steigt an, je verrauschter der verschränkte Zustand wird (bei zufälligen Ergebnissen mit einem Erwartungswert um 0 würde sich der MSE der 1 annähern). Der Code berechnet diesen MSE für jede Gruppe von vier Qubits. Da kein Rauschen vorliegt, erhalten wir in diesem Beispiel mit einem Simulator im kleinen Maßstab erwartungsgemäß MSE = 0.
Beispiel für groß angelegte Hardware
Hier fügen wir nun all diese Details zu einem einzigen, groß angelegten Arbeitsablauf zusammen, der anschließend auf echter Quantenhardware ausgeführt wird.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)Wir durchsuchen die Kopplungskarte des Geräts programmgesteuert nach allen linear verbundenen Ketten aus vier Qubits. Jede dieser Ketten (bezeichnet durch die Qubit-Indizes ) dient als Testfall für die Verschränkungs-Swap-Schaltung. Indem wir alle möglichen Pfade für die Quanten- length-4 s (QIP) ermitteln, stellen wir eine maximale Abdeckung für mögliche Gruppierungen von Qubits sicher, mit denen das Protokoll realisiert werden könnte.
Wir erzeugen diese Ketten mithilfe einer Hilfsfunktion, die eine gierige Suche im Gerätediagramm durchführt. Es gibt „Streifen“ aus vier 4-Qubit-Ketten zurück, die zu 16-Qubit-Gruppen gebündelt sind. Durch das Bündeln können wir mehrere Vier-Qubit-Experimente parallel auf verschiedenen Teilen des Chips durchführen und das gesamte Gerät effizient nutzen. Jeder 16-Qubit-Streifen enthält vier voneinander unabhängige Ketten, was bedeutet, dass innerhalb dieser Gruppe kein Qubit mehrfach verwendet wird. Ein Streifen könnte beispielsweise aus den Ketten , , und bestehen, die alle gebündelt sind. Jedes Qubit, das nicht in einem Streifen enthalten war, wird in der leftover Variablen 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, leftoversinitial_layouts, leftover = stripes16_from_backend(backend)create_bell_stab()Als Nächstes bauen wir die Schaltung für jeden 16-Qubit-Streifen mithilfe der Funktion auf. Am Ende dieses Schritts verfügen wir über eine Liste von Schaltkreisen, die jede Vier-Qubit-Kette auf dem Gerät abdecken. Anschließend transpilieren wir die Schaltkreise, führen sie auf dem eigentlichen Backend aus und werten die Ergebnisse aus.
# -------------------------Step 1-------------------------
circuits = create_bell_stab(initial_layouts)
# -------------------------Step 2-------------------------
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)
# -------------------------Step 3-------------------------
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["TUT_BDC"]
job = sampler.run(isa_circuits)
# -------------------------Step 4-------------------------
layouts_mse = get_mse(job.result(), initial_layouts)Output:
layout [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
qubits: [0, 1, 2, 3], mse:, 0.6302
qubits: [4, 5, 6, 7], mse:, 0.0949
qubits: [8, 9, 10, 11], mse:, 0.1729
qubits: [12, 13, 14, 15], mse:, 0.0473
layout [16, 23, 22, 21, 17, 27, 26, 25, 18, 31, 30, 29, 19, 35, 34, 33]
qubits: [16, 23, 22, 21], mse:, 0.0533
qubits: [17, 27, 26, 25], mse:, 0.2966
qubits: [18, 31, 30, 29], mse:, 0.0447
qubits: [19, 35, 34, 33], mse:, 0.0392
layout [36, 41, 42, 43, 37, 45, 46, 47, 38, 49, 50, 51, 39, 53, 54, 55]
qubits: [36, 41, 42, 43], mse:, 0.1577
qubits: [37, 45, 46, 47], mse:, 0.0705
qubits: [38, 49, 50, 51], mse:, 0.2914
qubits: [39, 53, 54, 55], mse:, 0.1711
layout [56, 63, 62, 61, 57, 67, 66, 65, 58, 71, 70, 69, 59, 75, 74, 73]
qubits: [56, 63, 62, 61], mse:, 0.1236
qubits: [57, 67, 66, 65], mse:, 0.9969
qubits: [58, 71, 70, 69], mse:, 0.0631
qubits: [59, 75, 74, 73], mse:, 0.0301
layout [76, 81, 82, 83, 77, 85, 86, 87, 78, 89, 90, 91, 79, 93, 94, 95]
qubits: [76, 81, 82, 83], mse:, 0.2787
qubits: [77, 85, 86, 87], mse:, 0.0497
qubits: [78, 89, 90, 91], mse:, 0.1271
qubits: [79, 93, 94, 95], mse:, 0.0468
layout [96, 103, 102, 101, 97, 107, 106, 105, 98, 111, 110, 109, 99, 115, 114, 113]
qubits: [96, 103, 102, 101], mse:, 0.8657
qubits: [97, 107, 106, 105], mse:, 0.0399
qubits: [98, 111, 110, 109], mse:, 0.0667
qubits: [99, 115, 114, 113], mse:, 0.2444
layout [116, 121, 122, 123, 117, 125, 126, 127, 118, 129, 130, 131, 119, 133, 134, 135]
qubits: [116, 121, 122, 123], mse:, 0.0429
qubits: [117, 125, 126, 127], mse:, 0.0487
qubits: [118, 129, 130, 131], mse:, 0.0823
qubits: [119, 133, 134, 135], mse:, 0.0583
layout [136, 143, 142, 141, 137, 147, 146, 145, 138, 151, 150, 149, 139, 155, 154, 153]
qubits: [136, 143, 142, 141], mse:, 0.0209
qubits: [137, 147, 146, 145], mse:, 0.0384
qubits: [138, 151, 150, 149], mse:, 0.4941
qubits: [139, 155, 154, 153], mse:, 0.1062
Die Ergebnisse zeigen eine große Bandbreite an Verschränkungsqualitäten innerhalb des Geräts. Dies bestätigt die Erkenntnis der Studie, dass es je nach den verwendeten physikalischen Qubits zu Abweichungen in der Bell-Zustandsgenauigkeit um mehr als eine Größenordnung kommen kann. Praktisch bedeutet dies, dass bestimmte Regionen oder Verbindungen im Chip viel besser für Messungen während des Betriebs und Feedforward-Operationen geeignet sind als andere. Faktoren wie Qubit-Auslesefehler, Qubit-Lebensdauer und Übersprechen tragen wahrscheinlich zu diesen Unterschieden bei. Wenn beispielsweise eine Kette ein besonders rauschbehaftetes Auslese-Qubit enthält, könnte die Messung in der Mitte des Schaltkreises unzuverlässig sein, was zu einer schlechten Genauigkeit für dieses verschränkte Paar (hohe MSE) führen würde.
Schließlich visualisieren wir die Gesamtleistung, indem wir die kumulative Verteilungsfunktion (CDF) der MSE-Werte für alle Ketten grafisch darstellen. Das CDF-Diagramm zeigt den MSE-Schwellenwert auf der x-Achse und den Anteil der Qubit-Paare, die höchstens diesen MSE-Wert aufweisen, auf der y-Achse. Diese Kurve beginnt bei Null und nähert sich Eins, wenn der Schwellenwert wächst, um alle Datenpunkte zu erfassen. Ein steiler Anstieg bei einem niedrigen MSE würde darauf hindeuten, dass viele Paare eine hohe Genauigkeit aufweisen; ein langsamer Anstieg bedeutet, dass viele Paare größere Fehler aufweisen. Wir versehen die CDF mit Anmerkungen zu den Identitäten der besten Paare. In der Darstellung entspricht jeder Punkt in der CDF der MSE einer Vier-Qubit-Kette, und wir kennzeichnen den Punkt mit dem Paar von Qubit-Indizes, die in diesem Experiment verschränkt waren. Dadurch lässt sich leicht erkennen, welche physikalischen Qubit-Paare die beste Leistung erzielen (die Punkte ganz links auf der CDF).
plot_mse_ecdfs(layouts_mse, combine_layouts=True)Output:
Nächste Schritte
Wenn Sie diese Arbeit interessant fanden, könnten Sie auch an folgenden Materialien interessiert sein:
- Erfahren Sie, wie Sie Fernverschränkung mit dynamischen Schaltkreisen realisieren können
- Erfahren Sie, wie Sie den „Kicked“-Ising-Hamiltonian mit dynamischen Schaltkreisen simulieren können