CHSH-Ungleichheit
Geschätzte Laufzeit: Zwei Minuten auf einem Heron- r3 -Prozessor (HINWEIS: Dies ist lediglich eine Schätzung. (Die Laufzeit kann variieren.)
Lernziele
- Wie man eine parametrisierte CHSH-Schaltung im Bell-Zustand aufbaut und die vier Erwartungswerte misst, aus denen sich die CHSH-Zeugen zusammensetzen.
- Wie berechnet man die Erwartungswerte mehrerer Observablen bei einem Parameterdurchlauf in einem einzigen Aufruf der Primitivfunktion
EstimatorV2? - Wie man einen Quanten-Workflow auf einem verrauschten lokalen Simulator validiert,
AerSimulator.from_backendbevor man ihn an die Hardware übergibt. - Wie man ein CHSH-Experiment zu einem geräteweiten Verschränkungs-Benchmark skaliert, indem man viele unabhängige Bell-Paare parallel auf „ IBM Quantum® “-Hardware ausführt.
Voraussetzungen
- Verschränkung in der Praxis – eine Unterrichtseinheit über Bell-Zustände und das CHSH-Spiel.
SparsePauliOpsowie die Einführung in „Qiskit primitives “.
Hintergrund
In diesem Tutorium werden Sie ein Experiment auf einem Quantencomputer durchführen, um die Verletzung der CHSH-Ungleichung mit dem Estimator-Primitiv zu demonstrieren.
Die nach Clauser, Horne, Shimony und Holt benannte CHSH-Ungleichung wird verwendet, um Bells Theorem (1969) experimentell zu überprüfen. Der Satz besagt, dass lokale Theorien mit versteckten Variablen bestimmte Folgen der Verschränkung in der Quantenmechanik nicht erklären können. Der Nachweis einer Verletzung der CHSH-Ungleichung zeigt, dass die Quantenmechanik mit Theorien lokaler versteckter Variablen unvereinbar ist – ein Experiment, das für unser Verständnis der Quantenmechanik von grundlegender Bedeutung ist.
Der Nobelpreis für Physik 2022 wurde an Alain Aspect, John Clauser und Anton Zeilinger verliehen, unter anderem für ihre bahnbrechenden Arbeiten auf dem Gebiet der Quanteninformationswissenschaft und insbesondere für ihre Experimente mit verschränkten Photonen, mit denen sie die Verletzung der Bellschen Ungleichungen nachweisen konnten.
Für dieses Experiment werden wir ein verschränktes Paar erzeugen, bei dem wir jedes Qubit in zwei verschiedenen Basen messen. Wir bezeichnen die Basen für das erste Qubit mit und und die Basen für das zweite Qubit mit und . Auf diese Weise können wir die CHSH-Größe berechnen:
Jede Beobachtungsgröße ist entweder „ “ oder „ “. Offensichtlich muss einer der Terme „ “ gleich „ “ sein, während der andere gleich „ “ sein muss. Daher gilt: „ “. Der Mittelwert von „ “ muss die folgende Ungleichung erfüllen:
Die Erweiterung von „ “ in Bezug auf „ “, „ “, „ “ und „ “ ergibt:
Sie können eine andere CHSH-Menge definieren :
was zu einer weiteren Ungleichung führt:
Wenn sich die Quantenmechanik durch Theorien mit lokalen versteckten Variablen beschreiben ließe, würden diese Ungleichungen immer gelten. Wie in diesem Tutorial gezeigt wurde, können diese auf einem Quantencomputer verletzt werden, sodass die Quantenmechanik nicht mit Theorien lokaler versteckter Variablen vereinbar ist.
Wir erzeugen das verschränkte Paar, indem wir den Bell-Zustand vorbereiten. Mithilfe der Estimator-Primitive erhalten wir die Erwartungswerte und direkt, ohne sie aus den Rohzählwerten rekonstruieren zu müssen. Wir messen das zweite Qubit in den Basiszuständen „ “ und „ “. Das erste Qubit wird ebenfalls in orthogonalen Basen gemessen, jedoch mit einem Rotationswinkel , den wir zwischen und variieren. Die Estimator-Primitive wertet diese Parametervariation in einem einzigen primitiven Unified Bloc ( PUB ) aus.
Anforderungen
Bevor Sie mit dieser Anleitung beginnen, stellen Sie bitte sicher, dass Folgendes installiert ist:
- Qiskit SDK v2.0 oder höher, mit Visual isierungsunterstützung
- Qiskit Runtime v0.40 oder später (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 oder höher (
pip install qiskit-aer)
Einrichtung
# General
import numpy as np
# Qiskit imports
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# `qiskit-ibm-runtime` imports
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
# Qiskit Aer for local noisy simulation
from qiskit_aer import AerSimulator
# Plotting routines
import matplotlib.pyplot as plt
import matplotlib.ticker as tck# Select an IBM Quantum backend.
service = QiskitRuntimeService()
backend = service.least_busy(
min_num_qubits=127, operational=True, simulator=False
)
backend.nameOutput:
'ibm_pittsburgh'
Beispiel für einen Simulator im kleinen Maßstab
Bevor wir einen Hardware-Auftrag einreichen, überprüfen wir den gesamten Arbeitsablauf auf einem lokalen Simulator mit Störsignalen. Wir verwenden AerSimulator.from_backend(backend) zur Erstellung eines Simulators, der das Rauschmodell und die Kopplungskarte des von Ihnen ausgewählten Backends übernimmt, sodass das Simulatorverhalten qualitativ dem entspricht, was wir von der Hardware erwarten.
Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen
Wir beschreiben den CHSH-Schaltkreis mit einem einzigen Parameter , der die Messbasis des ersten Qubits durchläuft. Die Primitive Estimator vereinfacht die Analyse: Sie liefert direkt die Erwartungswerte der Observablen und kann eine parametrisierte Schaltung mit einem einzigen Aufruf für viele Parameterwerte auswerten.
theta = Parameter(r"$\theta$")
chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)
chsh_circuit.draw(output="mpl", idle_wires=False, style="iqp")Output:
Als Nächstes erstellen wir eine Liste mit 21 Phasenwerten von bis , bei denen die parametrisierte Schaltung ausgewertet werden soll ( , , , …, , ).
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
# Phases need to be expressed as a list of lists for the Estimator PUB
individual_phases = [[ph] for ph in phases]Abschließend definieren wir die Observablen. Das erste Qubit wird entlang von Achsen gemessen, die um gedreht sind; das zweite Qubit wird in und gemessen. Bei dieser Wahl entsprechen die vier CHSH-Korrelatoren den Pauli-Operatoren , , und :
# <S_1> = <ZZ> - <ZX> + <XZ> + <XX>
observable1 = SparsePauliOp.from_list(
[("ZZ", 1), ("ZX", -1), ("XZ", 1), ("XX", 1)]
)
# <S_2> = <ZZ> + <ZX> - <XZ> + <XX>
observable2 = SparsePauliOp.from_list(
[("ZZ", 1), ("ZX", 1), ("XZ", -1), ("XX", 1)]
)Schritt 2: Optimierung des Problems für die Ausführung auf Quantenhardware
V2 Primitive akzeptieren nur Schaltkreise und Observablen, die den vom Zielsystem unterstützten Befehlen und Verbindungsmöglichkeiten entsprechen (Befehlssatzarchitektur, kurz ISA, Schaltkreise und Observablen). Wir erstellen das AerSimulator Backend und transpilieren es für die Zielumgebung des Simulators, sodass derselbe Pass-Manager durchgängig zum Einsatz kommt.
# Build a noisy simulator from the ibm_pittsburgh backend
aer_sim = AerSimulator.from_backend(backend)
pm = generate_preset_pass_manager(target=aer_sim.target, optimization_level=3)
chsh_isa_circuit = pm.run(chsh_circuit)
chsh_isa_circuit.draw(output="mpl", idle_wires=False, style="iqp")Output:
SparsePauliOp.apply_layoutAußerdem wandeln wir die Observablen mithilfe von so um, dass sie zum Qubit-Layout der transpilierten Schaltung passen.
isa_observable1 = observable1.apply_layout(layout=chsh_isa_circuit.layout)
isa_observable2 = observable2.apply_layout(layout=chsh_isa_circuit.layout)Schritt 3: Mit „ Qiskit primitives “ ausführen
Führen Sie den Parameter-Sweep im aer_sim``EstimatorV2 Modus durch. Die Methode „Estimator“ run() nimmt eine iterierbare Struktur aus PUBs entgegen. Jede „ PUB “-Datei hat das Format (circuit, observables, parameter_values, precision). Wir übergeben beide Observables gemeinsam, damit sie denselben Parameterdurchlauf nutzen.
# Use the AerSimulator-backed Estimator to validate the workflow locally
estimator_sim = Estimator(mode=aer_sim)
pub = (
chsh_isa_circuit, # ISA circuit
[[isa_observable1], [isa_observable2]], # ISA observables
individual_phases, # Parameter values
)
sim_result = estimator_sim.run(pubs=[pub]).result()Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
Der Schätzer liefert Erwartungswerte für beide Observablen. Wir stellen sie auf zusammen mit der klassischen Schranke ( ) und der Tsirelson-Schranke ( ) grafisch dar. Die grau schattierten Bereiche markieren die Lücke zwischen den beiden. Punkte, die innerhalb dieser Bänder liegen, verstoßen gegen die CHSH-Ungleichung.
chsh1_sim = sim_result[0].data.evs[0]
chsh2_sim = sim_result[0].data.evs[1]
def plot_chsh(phases, chsh1, chsh2, title):
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(
phases / np.pi, chsh1, "o-", label=r"$\langle S_1 \rangle$", zorder=3
)
ax.plot(
phases / np.pi, chsh2, "o-", label=r"$\langle S_2 \rangle$", zorder=3
)
# classical bound +-2
ax.axhline(y=2, color="0.9", linestyle="--")
ax.axhline(y=-2, color="0.9", linestyle="--")
# quantum bound, +-2*sqrt(2)
ax.axhline(y=np.sqrt(2) * 2, color="0.9", linestyle="-.")
ax.axhline(y=-np.sqrt(2) * 2, color="0.9", linestyle="-.")
ax.fill_between(phases / np.pi, 2, 2 * np.sqrt(2), color="0.6", alpha=0.7)
ax.fill_between(
phases / np.pi, -2, -2 * np.sqrt(2), color="0.6", alpha=0.7
)
ax.xaxis.set_major_formatter(tck.FormatStrFormatter("%g $\\pi$"))
ax.xaxis.set_major_locator(tck.MultipleLocator(base=0.5))
ax.set_xlabel(r"$\theta$")
ax.set_ylabel("CHSH witness")
ax.set_title(title)
ax.legend()
plt.show()
plot_chsh(
phases,
chsh1_sim,
chsh2_sim,
"CHSH witnesses from AerSimulator (ibm_pittsburgh noise model)",
)Output:
Die CHSH-Werte des Simulators überschreiten bereits bei mehreren Werten von die klassische Grenze von , selbst unter Berücksichtigung des Rauschmodells des Backends. Die Spitzenwerte liegen aufgrund des simulierten Geräteraums knapp unter der Tsirelson-Grenze . Nachdem der Arbeitsablauf validiert wurde, wenden wir uns nun der eigentlichen Hardware zu.
Beispiel für groß angelegte Hardware
Ein CHSH-Test ist von Natur aus ein Zwei-Qubit -Experiment, daher lässt sich seine Skalierbarkeit nicht durch die Vergrößerung einer einzelnen Schaltung erreichen. Stattdessen wird die Skalierbarkeit dadurch erreicht, dass viele Tests parallel ausgeführt werden. Hier unterteilen wir das Backend in so viele disjunkte Bell-Paare, wie es die Konnektivität zulässt (eine Zuordnung der Kopplungsabbildung), und führen für jedes Paar einen unabhängigen CHSH-Teilschaltkreis aus – alles in einem einzigen Job.
Damit wird CHSH zu einem geräteweiten Maßstab für die Verschränkungsqualität : Anstatt ein einzelnes, sorgfältig ausgewähltes Paar zu untersuchen, testen wir die Verschränkung über einen großen Teil des Chips hinweg auf einmal – unter realistischen Bedingungen, bei denen jedes Paar mit dem Übersprechen seiner Nachbarn und Fehlern bei parallelen Gattern zu kämpfen hat. Die gleichzeitige Verletzung der Ungleichung für jedes Paar belegt, dass überall auf dem Gerät echte Verschränkung vorliegt.
# -------------------------Step 1: Map classical inputs to a quantum problem-------------------------
# A CHSH test is bipartite, so we scale up by running one independent CHSH
# experiment on every disjoint Bell pair the device can host. A greedy
# matching of the coupling map gives a set of edges that share no qubits.
num_qubits = backend.num_qubits
used = set()
pairs = []
for qa, qb in backend.coupling_map.get_edges():
if qa not in used and qb not in used:
pairs.append((qa, qb))
used.update((qa, qb))
num_pairs = len(pairs)
print(
f"Tiling {backend.name} with {num_pairs} parallel Bell pairs "
f"({2 * num_pairs} of {num_qubits} qubits)"
)
# One parameterized CHSH sub-circuit per pair, all sharing the angle theta
theta = Parameter(r"$\theta$")
chsh_circuit = QuantumCircuit(num_qubits)
for qa, qb in pairs:
chsh_circuit.h(qa)
chsh_circuit.cx(qa, qb)
chsh_circuit.ry(theta, qa)
# Embed the two CHSH observables onto each pair's qubits (identity elsewhere)
obs1 = SparsePauliOp.from_list([("ZZ", 1), ("ZX", -1), ("XZ", 1), ("XX", 1)])
obs2 = SparsePauliOp.from_list([("ZZ", 1), ("ZX", 1), ("XZ", -1), ("XX", 1)])
observables = []
for qa, qb in pairs:
observables.append([obs1.apply_layout([qa, qb], num_qubits)])
observables.append([obs2.apply_layout([qa, qb], num_qubits)])
number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]
# -------------------------Step 2: Optimize problem for quantum hardware execution-------------------------
pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [
[o[0].apply_layout(chsh_isa_circuit.layout)] for o in observables
]
# -------------------------Step 3: Execute using Qiskit primitives-------------------------
estimator_hw = Estimator(mode=backend)
estimator_hw.options.environment.job_tags = ["TUT_CI"]
pub = (chsh_isa_circuit, isa_observables, individual_phases)
job = estimator_hw.run(pubs=[pub])
print(f"Job ID: {job.job_id()}")
hw_result = job.result()
# -------------------------Step 4: Post-process and return result in desired classical format-------------------------
# evs has shape (2 * num_pairs, number_of_phases); rows alternate S1, S2
evs = np.asarray(hw_result[0].data.evs)
chsh1_all = evs[0::2]
chsh2_all = evs[1::2]
# A pair "violates" CHSH if its strongest witness exceeds the classical bound
peak = np.maximum(
np.abs(chsh1_all).max(axis=1), np.abs(chsh2_all).max(axis=1)
)
n_violate = int(np.sum(peak > 2))
print(
f"{n_violate}/{num_pairs} Bell pairs violated the CHSH inequality "
f"(mean peak witness {peak.mean():.2f}, classical bound 2)"
)
fig, ax = plt.subplots(figsize=(10, 6))
# Faint individual per-pair curves
for row in chsh1_all:
ax.plot(phases / np.pi, row, color="#1f77b4", alpha=0.2, lw=1)
for row in chsh2_all:
ax.plot(phases / np.pi, row, color="#ff7f0e", alpha=0.2, lw=1)
# Bold mean curves across all pairs
ax.plot(
phases / np.pi,
chsh1_all.mean(axis=0),
color="#1f77b4",
lw=2.5,
label=r"$\langle S_1 \rangle$ (mean)",
)
ax.plot(
phases / np.pi,
chsh2_all.mean(axis=0),
color="#ff7f0e",
lw=2.5,
label=r"$\langle S_2 \rangle$ (mean)",
)
# classical bound +-2 and Tsirelson bound +-2*sqrt(2)
ax.axhline(y=2, color="0.9", linestyle="--")
ax.axhline(y=-2, color="0.9", linestyle="--")
ax.axhline(y=np.sqrt(2) * 2, color="0.9", linestyle="-.")
ax.axhline(y=-np.sqrt(2) * 2, color="0.9", linestyle="-.")
ax.fill_between(phases / np.pi, 2, 2 * np.sqrt(2), color="0.6", alpha=0.7)
ax.fill_between(phases / np.pi, -2, -2 * np.sqrt(2), color="0.6", alpha=0.7)
ax.xaxis.set_major_formatter(tck.FormatStrFormatter("%g $\\pi$"))
ax.xaxis.set_major_locator(tck.MultipleLocator(base=0.5))
ax.set_xlabel(r"$\theta$")
ax.set_ylabel("CHSH witness")
ax.set_title(
f"CHSH witnesses for {num_pairs} parallel Bell pairs on {backend.name}"
)
ax.legend()
plt.show()Output:
Tiling ibm_pittsburgh with 64 parallel Bell pairs (128 of 156 qubits)
Job ID: d86efd5g7okc73el0rp0
63/64 Bell pairs violated the CHSH inequality (mean peak witness 2.75, classical bound 2)
Die blassen Kurven stellen die einzelnen Bell-Paare dar, während die fetten Kurven deren Mittelwert über das gesamte Gerät hinweg anzeigen. Jedes Paar beschreibt dieselbe Sinuskurve, wie sie von der Quantenmechanik vorhergesagt wird, und die Streuung zwischen den schwachen Kurven spiegelt die Schwankungen im Rauschen von Paar zu Paar wider. Überall dort, wo eine Kurve in die grauen Bereiche hineinragt, hat sie die klassische Grenze von überschritten, und die abgedruckte Zusammenfassung bestätigt, dass im Wesentlichen jedes Paar gleichzeitig gegen die CHSH-Ungleichung verstößt.
Die Spitzenwerte liegen aufgrund des Geräterauschens unterhalb der Tsirelson-Grenze , doch die Schlussfolgerung ist eindeutig: Das Backend hält echte Verschränkung gleichzeitig über den gesamten Chip hinweg aufrecht, nicht nur bei einem einzigen, sorgfältig ausgewählten Paar. In diesem Sinne lässt sich das CHSH-Experiment „skalieren“: nicht als eine einzige größere Schaltung, sondern als paralleler Maßstab, der die Verschränkung überall gleichzeitig nachweist.
Nächste Schritte
Wenn Sie diese Arbeit interessant fanden, könnten Sie auch an folgenden Materialien interessiert sein:
- Verschränkung in der Praxis : Eine Vorlesung von John Watrous über Bell-Zustände und das CHSH-Spiel.
- Erste Schritte mit der „Estimator“-Primitive : Ein Leitfaden zu PUBs und Parameter-Sweeps.
- Echtzeit-Benchmarking zur Qubit-Auswahl : eine weitere Methode zur Charakterisierung der Qubit- und Verschränkungsqualität in einem Gerät.
SparsePauliOpAPI-Referenz.