Nishimori-Phasenübergang
Geschätzter Aufwand: 3 Minuten auf einem Heron-r2-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine Laufzeit kann abweichen.)
Hintergrund
Dieses Tutorial zeigt, wie man einen Nishimori-Phasenübergang auf einem IBM®-Quantenprozessor realisiert. Das Experiment wurde ursprünglich in Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits beschrieben.
Der Nishimori-Phasenübergang bezeichnet den Übergang zwischen kurzreichweitig und langreichweitig geordneten Phasen im Random-Bond-Ising-Modell. Auf einem Quantencomputer manifestiert sich die langreichweitig geordnete Phase als ein Zustand, in dem Qubits über das gesamte Gerät verschränkt sind. Dieser hochverschränkte Zustand wird mit dem Protokoll zur Erzeugung von Verschränkung durch Messung (GEM) präpariert. Durch den Einsatz von Mid-Circuit-Messungen kann das GEM-Protokoll Qubits über das gesamte Gerät hinweg mit Schaltkreisen konstanter Tiefe verschränken. Dieses Tutorial verwendet die Implementierung des GEM-Protokolls aus dem Softwarepaket GEM Suite.
Anforderungen
Stelle vor Beginn dieses Tutorials sicher, dass Folgendes installiert ist:
- Qiskit SDK v1.0 oder neuer, mit Unterstützung für Visualisierung
- Qiskit Runtime v0.22 oder neuer (
pip install qiskit-ibm-runtime) - GEM Suite (
pip install gem-suite)
Einrichtung
# Added by doQumentation — required packages for this notebook
!pip install -q gem-suite matplotlib qiskit qiskit-ibm-runtime
import matplotlib.pyplot as plt
from collections import defaultdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.transpiler import generate_preset_pass_manager
from gem_suite import PlaquetteLattice
from gem_suite.experiments import GemExperiment
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
Das GEM-Protokoll arbeitet auf einem Quantenprozessor, dessen Qubit-Konnektivität durch ein Gitter beschrieben wird. Aktuelle IBM-Quantenprozessoren verwenden das Heavy-Hex-Gitter. Die Qubits des Prozessors werden anhand der Einheitszelle des Gitters, die sie besetzen, in Plaquettes gruppiert. Da ein Qubit in mehr als einer Einheitszelle vorkommen kann, sind die Plaquettes nicht disjunkt. Auf dem Heavy-Hex-Gitter enthält eine Plaquette 12 Qubits. Die Plaquettes bilden ihrerseits auch ein Gitter, wobei zwei Plaquettes verbunden sind, wenn sie Qubits teilen. Auf dem Heavy-Hex-Gitter teilen benachbarte Plaquettes jeweils 3 Qubits.
Im Softwarepaket GEM Suite ist die grundlegende Klasse zur Implementierung des GEM-Protokolls PlaquetteLattice, die das Gitter der Plaquettes repräsentiert (das sich vom Heavy-Hex-Gitter unterscheidet). Eine PlaquetteLattice kann aus einer Qubit-Kopplungsmap initialisiert werden. Derzeit werden nur Heavy-Hex-Kopplungsmaps unterstützt.
Die folgende Code-Zelle initialisiert ein Plaquette-Gitter aus der Kopplungsmap eines IBM-Quantenprozessors. Das Plaquette-Gitter umfasst nicht immer die gesamte Hardware. Beispielsweise hat ibm_torino insgesamt 133 Qubits, aber das größte Plaquette-Gitter, das auf das Gerät passt, verwendet nur 125 davon und umfasst insgesamt 18 Plaquettes. Ähnliches lässt sich auch bei IBM Quantum®-Geräten mit unterschiedlichen Qubit-Zahlen beobachten.
# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<YOUR_API_KEYN>", overwrite=True, set_as_default=True)
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)
print(f"Number of qubits in backend: {backend.num_qubits}")
print(
f"Number of qubits in plaquette lattice: {len(list(plaquette_lattice.qubits()))}"
)
print(f"Number of plaquettes: {len(list(plaquette_lattice.plaquettes()))}")
Number of qubits in backend: 133
Number of qubits in plaquette lattice: 125
Number of plaquettes: 18
Du kannst das Plaquette-Gitter visualisieren, indem du ein Diagramm seiner Graphdarstellung erzeugst. Im Diagramm werden die Plaquettes durch beschriftete Sechsecke dargestellt, und zwei Plaquettes sind durch eine Kante verbunden, wenn sie Qubits teilen.
plaquette_lattice.draw_plaquettes()
Mit der Methode plaquettes kannst du Informationen über einzelne Plaquettes abrufen, z. B. welche Qubits sie enthalten.
# Get a list of the plaquettes
plaquettes = list(plaquette_lattice.plaquettes())
# Display information about plaquette 0
plaquettes[0]
PyPlaquette(index=0, qubits=[0, 1, 2, 3, 4, 15, 16, 19, 20, 21, 22, 23], neighbors=[3, 1])
Du kannst auch ein Diagramm der zugrunde liegenden Qubits erzeugen, die das Plaquette-Gitter bilden.
plaquette_lattice.draw_qubits()

Neben den Qubit-Bezeichnungen und den Kanten, die anzeigen, welche Qubits verbunden sind, enthält das Diagramm drei weitere Informationen, die für das GEM-Protokoll relevant sind:
- Jedes Qubit ist entweder schattiert (grau) oder nicht schattiert. Die schattierten Qubits sind „Site"-Qubits, die die Gitterplätze des Ising-Modells repräsentieren, und die nicht schattierten Qubits sind „Bond"-Qubits, die zur Vermittlung von Wechselwirkungen zwischen den Site-Qubits dienen.
- Jedes Site-Qubit ist entweder mit (A) oder (B) beschriftet und gibt damit eine von zwei Rollen an, die ein Site-Qubit im GEM-Protokoll übernehmen kann (die Rollen werden später erläutert).
- Jede Kante ist in einer von sechs Farben eingefärbt, wodurch die Kanten in sechs Gruppen aufgeteilt werden. Diese Aufteilung bestimmt, wie Zwei-Qubit-Gates parallelisiert werden können, sowie verschiedene Scheduling-Muster, die auf einem verrauschten Quantenprozessor wahrscheinlich unterschiedlich viel Fehler verursachen. Da Kanten innerhalb einer Gruppe disjunkt sind, kann eine Schicht von Zwei-Qubit-Gates gleichzeitig auf diesen Kanten angewendet werden. Tatsächlich lassen sich die sechs Farben in drei Gruppen zu je zwei Farben aufteilen, sodass die Vereinigung jeder Gruppe von zwei Farben immer noch disjunkt ist. Daher werden nur drei Schichten von Zwei-Qubit-Gates benötigt, um jede Kante zu aktivieren. Es gibt 12 Möglichkeiten, die sechs Farben so aufzuteilen, und jede solche Aufteilung ergibt ein anderes 3-Schicht-Gate-Schedule.
Nachdem du ein Plaquette-Gitter erstellt hast, besteht der nächste Schritt darin, ein GemExperiment-Objekt zu initialisieren, dem sowohl das Plaquette-Gitter als auch das Backend übergeben werden, auf dem du das Experiment ausführen möchtest. Die Klasse GemExperiment verwaltet die eigentliche Implementierung des GEM-Protokolls, einschließlich der Erzeugung von Schaltkreisen, des Einreichens von Jobs und der Analyse der Daten. Die folgende Code-Zelle initialisiert die Experiment-Klasse, schränkt das Plaquette-Gitter dabei auf nur zwei der Plaquettes ein (21 Qubits), um die Größe des Experiments zu reduzieren und sicherzustellen, dass das Rauschen der Hardware das Signal nicht überwältigt.
gem_exp = GemExperiment(plaquette_lattice.filter([9, 12]), backend=backend)
# visualize the plaquette lattice after filtering
plaquette_lattice.filter([9, 12]).draw_qubits()

Ein GEM-Protokoll-Schaltkreis wird mithilfe der folgenden Schritte aufgebaut:
- Bereite den Zustand für alle Qubits vor, indem du auf jedes Qubit ein Hadamard-Gate anwendest.
- Wende ein -Gate zwischen jedem Paar verbundener Qubits an. Dies kann mit 3 Gate-Schichten erreicht werden. Jedes -Gate wirkt auf ein Site-Qubit und ein Bond-Qubit. Wenn das Site-Qubit mit (B) beschriftet ist, ist der Winkel auf festgelegt. Wenn das Site-Qubit mit (A) beschriftet ist, kann der Winkel variieren und so verschiedene Schaltkreise erzeugen. Standardmäßig ist der Winkelbereich auf 21 gleichmäßig verteilte Punkte zwischen und (einschließlich) eingestellt.
- Miss jedes Bond-Qubit in der Pauli--Basis. Da Qubits in der Pauli--Basis gemessen werden, kann dies erreicht werden, indem vor der Messung des Qubits ein Hadamard-Gate angewendet wird.
Beachte, dass das in der Einleitung dieses Tutorials zitierte Paper eine andere Konvention für den -Winkel verwendet, die sich von der in diesem Tutorial verwendeten Konvention um einen Faktor 2 unterscheidet.
In Schritt 3 werden nur die Bond-Qubits gemessen. Um zu verstehen, in welchem Zustand die Site-Qubits verbleiben, ist es hilfreich, den Fall zu betrachten, dass der in Schritt 2 auf Site-Qubits (A) angewendete -Winkel gleich ist. In diesem Fall befinden sich die Site-Qubits in einem hochverschränkten Zustand ähnlich dem GHZ-Zustand,
Aufgrund der Zufälligkeit der Messergebnisse kann der tatsächliche Zustand der Site-Qubits ein anderer Zustand mit langreichweitiger Ordnung sein, beispielsweise . Der GHZ-Zustand kann jedoch durch eine Dekodierungsoperation auf Basis der Messergebnisse wiederhergestellt werden. Wenn der -Winkel von heruntergeregelt wird, kann die langreichweitige Ordnung noch bis zu einem kritischen Winkel wiederhergestellt werden, der ohne Rauschen bei ungefähr liegt. Unterhalb dieses Winkels weist der resultierende Zustand keine langreichweitige Verschränkung mehr auf. Dieser Übergang zwischen dem Vorhandensein und dem Fehlen langreichweitiger Ordnung ist der Nishimori-Phasenübergang.
In der obigen Beschreibung wurden die Site-Qubits ungemessen gelassen, und die Dekodierungsoperation kann durch Anwenden von Quantengattern durchgeführt werden. In dem Experiment, wie es in der GEM Suite implementiert ist und dem dieses Tutorial folgt, werden die Site-Qubits tatsächlich gemessen, und die Dekodierungsoperation wird in einem klassischen Nachverarbeitungsschritt angewendet.
In der obigen Beschreibung kann die Dekodierungsoperation durch Anwenden von Quantengattern auf die Site-Qubits durchgeführt werden, um den Quantenzustand wiederherzustellen. Wenn das Ziel jedoch darin besteht, den Zustand sofort zu messen – beispielsweise zu Charakterisierungszwecken –, dann werden die Site-Qubits zusammen mit den Bond-Qubits gemessen, und die Dekodierungsoperation kann in einem klassischen Nachverarbeitungsschritt angewendet werden. So ist das Experiment in der GEM Suite implementiert, der dieses Tutorial folgt.
Zusätzlich zur Abhängigkeit vom -Winkel in Schritt 2, der standardmäßig über 21 Werte durchläuft, hängt der GEM-Protokoll-Schaltkreis auch vom verwendeten Scheduling-Muster ab, mit dem die 3 Schichten von -Gates implementiert werden. Wie bereits erwähnt, gibt es 12 solcher Scheduling-Muster. Daher beträgt die Gesamtanzahl der Schaltkreise im Experiment .
Die Schaltkreise des Experiments können mit der Methode circuits der Klasse GemExperiment erzeugt werden.
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
Total number of circuits: 252
Für die Zwecke dieses Tutorials reicht es aus, nur ein einziges Scheduling-Muster zu betrachten. Die folgende Code-Zelle schränkt das Experiment auf das erste Scheduling-Muster ein. Dadurch hat das Experiment nur noch 21 Schaltkreise, einen für jeden durchlaufenen -Winkel.
# Restrict experiment to the first scheduling pattern
gem_exp.set_experiment_options(schedule_idx=0)
# There are less circuits now
circuits = gem_exp.circuits()
print(f"Total number of circuits: {len(circuits)}")
# Print the RZZ angles swept over
print(f"RZZ angles:\n{gem_exp.parameters()}")
Total number of circuits: 21
RZZ angles:
[0. 0.07853982 0.15707963 0.23561945 0.31415927 0.39269908
0.4712389 0.54977871 0.62831853 0.70685835 0.78539816 0.86393798
0.9424778 1.02101761 1.09955743 1.17809725 1.25663706 1.33517688
1.41371669 1.49225651 1.57079633]
Die folgende Code-Zelle zeichnet ein Diagramm des Schaltkreises mit Index 5. Um die Größe des Diagramms zu reduzieren, werden die Messgatter am Ende des Schaltkreises entfernt.
# Get the circuit at index 5
circuit = circuits[5]
# Remove the final measurements to ease visualization
circuit.remove_final_measurements()
# Draw the circuit
circuit.draw("mpl", fold=-1, scale=0.5)
Schritt 2: Problem für die Ausführung auf Quantenhardware optimieren
Das Transpilieren von Quantenschaltkreisen für die Ausführung auf Hardware umfasst typischerweise eine Reihe von Phasen. Die Phasen, die den größten Rechenaufwand verursachen, sind in der Regel die Auswahl des Qubit-Layouts, das Routing der Zwei-Qubit-Gates gemäß der Qubit-Konnektivität der Hardware sowie die Optimierung des Schaltkreises zur Minimierung der Gate-Anzahl und -Tiefe. Im GEM-Protokoll sind die Layout- und Routing-Phasen nicht erforderlich, da die Hardware-Konnektivität bereits in den Entwurf des Protokolls eingeflossen ist. Die Schaltkreise haben bereits ein Qubit-Layout, und die Zwei-Qubit-Gates sind bereits auf native Verbindungen abgebildet. Darüber hinaus sollte, um die Struktur des Schaltkreises bei Variation des -Winkels zu erhalten, nur eine sehr grundlegende Schaltkreisoptimierung durchgeführt werden.
Die Klasse GemExperiment transpiliert Schaltkreise bei der Ausführung des Experiments transparent. Die Layout- und Routing-Phasen sind standardmäßig bereits so überschrieben, dass sie nichts tun, und die Schaltkreisoptimierung wird auf einem Niveau durchgeführt, das nur Einzelqubit-Gates optimiert. Du kannst jedoch zusätzliche Optionen mit der Methode set_transpile_options überschreiben oder übergeben. Zur Veranschaulichung transpiliert die folgende Code-Zelle den zuvor angezeigten Schaltkreis manuell und zeichnet den transpilierten Schaltkreis.
# Demonstrate setting transpile options
gem_exp.set_transpile_options(
optimization_level=1 # This is the default optimization level
)
pass_manager = generate_preset_pass_manager(
backend=backend,
initial_layout=list(gem_exp.physical_qubits),
**dict(gem_exp.transpile_options),
)
transpiled = pass_manager.run(circuit)
transpiled.draw("mpl", idle_wires=False, fold=-1, scale=0.5)

Schritt 3: Ausführung mit Qiskit-Primitiven
Um die GEM-Protokoll-Schaltkreise auf der Hardware auszuführen, rufe die Methode run des GemExperiment-Objekts auf. Du kannst die Anzahl der Shots angeben, die du aus jedem Schaltkreis sampeln möchtest. Die Methode run gibt ein ExperimentData-Objekt zurück, das du in einer Variablen speichern solltest. Beachte, dass die Methode run Jobs nur einreicht, ohne auf deren Abschluss zu warten – es handelt sich also um einen nicht-blockierenden Aufruf.
exp_data = gem_exp.run(shots=10_000)
Um auf die Ergebnisse zu warten, rufe die Methode block_for_results des ExperimentData-Objekts auf. Dieser Aufruf lässt den Interpreter warten, bis die Jobs abgeschlossen sind.
exp_data.block_for_results()
ExperimentData(GemExperiment, d0d5880a-34c1-4aab-a7b6-c4f58516bc03, job_ids=['cwg12ptmptp00082khhg'], metadata=<5 items>, figure_names=['two_point_correlation.svg', 'normalized_variance.svg', 'plaquette_ops.svg', 'bond_ops.svg'])
Schritt 4: Nachverarbeitung und Ausgabe im gewünschten klassischen Format
Bei einem -Winkel von wäre der dekodierte Zustand ohne Rauschen der GHZ-Zustand. Die langreichweitige Ordnung des GHZ-Zustands kann durch Darstellung der Magnetisierung der gemessenen Bitstrings visualisiert werden. Die Magnetisierung ist definiert als die Summe der Einzel-Qubit-Pauli--Operatoren,
wobei die Anzahl der Site-Qubits ist. Ihr Wert für einen Bitstring ist gleich der Differenz zwischen der Anzahl der Nullen und der Anzahl der Einsen. Die Messung des GHZ-Zustands liefert mit gleicher Wahrscheinlichkeit den Zustand aus lauter Nullen oder den Zustand aus lauter Einsen, sodass die Magnetisierung halb der Zeit und die andere Hälfte der Zeit wäre. Bei Fehlern durch Rauschen würden zwar auch andere Werte auftreten, aber wenn das Rauschen nicht zu groß ist, wäre die Verteilung immer noch nahe bei und konzentriert.
Für die rohen Bitstrings vor der Dekodierung wäre die Verteilung der Magnetisierung äquivalent zu der gleichmäßig zufälliger Bitstrings, ohne Rauschen.
Die folgende Code-Zelle stellt die Magnetisierung der rohen Bitstrings und der dekodierten Bitstrings beim -Winkel von dar.
def magnetization_distribution(
counts_dict: dict[str, int],
) -> dict[str, float]:
"""Compute magnetization distribution from counts dictionary."""
# Construct dictionary from magnetization to count
mag_dist = defaultdict(float)
for bitstring, count in counts_dict.items():
mag = bitstring.count("0") - bitstring.count("1")
mag_dist[mag] += count
# Normalize
shots = sum(counts_dict.values())
for mag in mag_dist:
mag_dist[mag] /= shots
return mag_dist
# Get counts dictionaries with and without decoding
data = exp_data.data()
# Get the last data point, which is at the angle for the GHZ state
raw_counts = data[-1]["counts"]
# Without decoding
site_indices = [
i for i, q in enumerate(gem_exp.plaquettes.qubits()) if q.role == "Site"
]
site_raw_counts = defaultdict(int)
for key, val in raw_counts.items():
site_str = "".join(key[-1 - i] for i in site_indices)
site_raw_counts[site_str] += val
# With decoding
_, site_decoded_counts = gem_exp.plaquettes.decode_outcomes(
raw_counts, return_counts=True
)
# Compute magnetization distribution
raw_magnetization = magnetization_distribution(site_raw_counts)
decoded_magnetization = magnetization_distribution(site_decoded_counts)
# Plot
plt.bar(*zip(*raw_magnetization.items()), label="raw")
plt.bar(*zip(*decoded_magnetization.items()), label="decoded", width=0.3)
plt.legend()
plt.xlabel("Magnetization")
plt.ylabel("Frequency")
plt.title("Magnetization distribution with and without decoding")
Text(0.5, 1.0, 'Magnetization distribution with and without decoding')
Um die langreichweitige Ordnung strenger zu charakterisieren, kannst du die mittlere Zwei-Punkt-Korrelation betrachten, definiert als
Ein höherer Wert zeigt einen höheren Grad an Verschränkung an. Die Klasse GemExperiment berechnet diesen Wert für die dekodierten Bitstrings automatisch als Teil der Verarbeitung der Experimentdaten. Sie speichert eine Abbildung, die über die Methode figure der Experimentdaten-Klasse zugänglich ist. In diesem Fall heißt die Abbildung two_point_correlation.
exp_data.figure("two_point_correlation")
Um den kritischen Punkt des Nishimori-Phasenübergangs zu bestimmen, kannst du die normierte Varianz von betrachten, definiert als
die das Ausmaß der Fluktuationen in der quadratischen Magnetisierung quantifiziert. Dieser Wert ist am kritischen Punkt des Nishimori-Phasenübergangs maximal. Ohne Rauschen liegt der kritische Punkt bei ungefähr . In Gegenwart von Rauschen verschiebt sich der kritische Punkt nach oben, aber der Phasenübergang ist noch beobachtbar, solange der kritische Punkt unterhalb von liegt.
exp_data.figure("normalized_variance")
Experiment skalieren
Die folgenden Code-Zellen führen das Experiment für sechs Plaquettes (49 Qubits) und die vollen 12 Plaquettes (125 Qubits) aus und stellen die normierte Varianz dar. Je größer das Experiment skaliert wird, desto stärker verschiebt die größere Rauschmenge den kritischen Punkt nach rechts.
gem_exp = GemExperiment(
plaquette_lattice.filter(range(3, 9)), backend=backend
)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")
gem_exp = GemExperiment(plaquette_lattice, backend=backend)
gem_exp.set_experiment_options(schedule_idx=0)
exp_data = gem_exp.run(shots=10_000)
exp_data.block_for_results()
exp_data.figure("normalized_variance")
Fazit
In diesem Tutorial hast du einen Nishimori-Phasenübergang auf einem Quantenprozessor mithilfe des GEM-Protokolls realisiert. Die Kenngrößen, die du bei der Nachverarbeitung untersucht hast – insbesondere die Zwei-Punkt-Korrelation und die normierte Varianz – dienen als Benchmarks für die Fähigkeit des Geräts, langreichweitig verschränkte Zustände zu erzeugen. Diese Benchmarks erweitern den Nutzen des GEM-Protokolls über die Untersuchung interessanter Physik hinaus. Im Rahmen des Protokolls hast du Qubits über das gesamte Gerät hinweg mit Schaltkreisen konstanter Tiefe verschränkt. Diese Leistung ist nur dank der Verwendung von Mid-Circuit-Messungen durch das Protokoll möglich. In diesem Experiment wurde der verschränkte Zustand sofort gemessen, aber eine interessante Möglichkeit zur weiteren Erkundung wäre es, den Zustand in weiterer Quantenverarbeitung zu verwenden!
Tutorial-Umfrage
Bitte nimm an dieser kurzen Umfrage teil, um Feedback zu diesem Tutorial zu geben. Deine Einblicke helfen uns, unsere Inhalte und die Benutzererfahrung zu verbessern.