Нерівність CHSH
Оцінка використання: Дві хвилини на процесорі Heron r3 (ПРИМІТКА: Це лише оцінка. Твій час виконання може відрізнятися.)
Результати навчання
Після завершення цього посібника ти зможеш розуміти таке:
- Як побудувати параметризовану схему CHSH зі станом Белла та виміряти чотири очікувані значення, що складають свідків CHSH.
- Як обчислити очікувані значення кількох спостережуваних величин при параметричному скануванні в одному виклику до примітиву
EstimatorV2. - Як перевірити квантовий робочий процес на локальному зашумленому симуляторі з
AerSimulator.from_backendперед відправкою на обладнання. - Як масштабувати експеримент CHSH до загальнопристройного бенчмарку заплутаності, запускаючи багато незалежних пар Белла паралельно на обладнанні IBM Quantum®.
Передумови
Рекомендується ознайомитися з такими темами:
- Заплутаність в дії — урок курсу про стани Белла та гру CHSH.
SparsePauliOpта вступ до примітивів Qiskit.
Загальна інформація
У цьому посібнику ти виконаєш експеримент на квантовому комп'ютері, щоб продемонструвати порушення нерівності CHSH за допомогою примітиву Estimator.
Нерівність CHSH, названа на честь авторів Клаузера, Хорна, Шимоні та Холта, використовується для експериментального доведення теореми Белла (1969). Ця теорема стверджує, що теорії локальних прихованих змінних не можуть пояснити деякі наслідки заплутаності в квантовій механіці. Порушення нерівності CHSH демонструє, що квантова механіка несумісна з теоріями локальних прихованих змінних — це фундаментальний для нашого розуміння квантової механіки експеримент.
Нобелівську премію з фізики 2022 року було присуджено Алену Аспе, Джону Клаузеру та Антону Цайлінгеру частково за їхню піонерську роботу в квантовій інформатиці, зокрема за експерименти з заплутаними фотонами, що демонструють порушення нерівностей Белла.
Для цього експерименту ми створимо заплутану пару, де будемо вимірювати кожен Qubit у двох різних базисах. Ми позначимо базиси для першого Qubit і , а базиси для другого Qubit і . Це дозволяє нам обчислити величину CHSH :
Кожна спостережувана величина дорівнює або , або . Очевидно, один з членів повинен дорівнювати , а інший має бути . Отже, . Середнє значення має задовольняти нерівність:
Розкладання через , , і дає:
Ти можеш визначити іншу величину CHSH :
що призводить до іншої нерівності:
Якщо квантову механіку можна описати теоріями локальних прихованих змінних, ці нерівності виконувалися б завжди. Як демонструється в цьому посібнику, вони можуть бути порушені на квантовому комп'ютері, тобто квантова механіка несумісна з теоріями локальних прихованих змінних.
Ми створимо заплутану пару, підготувавши стан Белла . Використовуючи примітив Estimator, ми отримаємо очікувані значення та безпосередньо, без реконструкції з необроблених підрахунків. Ми вимірюємо другий Qubit у базисах і . Перший Qubit також вимірюється в ортогональних базисах, але з кутом обертання , який ми змінюємо від до . Примітив Estimator обчислює це параметричне сканування в одному примітивному уніфікованому блоці (PUB).
Вимоги
Перед початком цього посібника переконайся, що у тебе встановлено таке:
- Qiskit SDK v2.0 або новіший, з підтримкою візуалізації
- Qiskit Runtime v0.40 або новіший (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 або новіший (
pip install qiskit-aer)
Налаштування
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
# 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 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.name
'ibm_pittsburgh'
Приклад на невеликому симуляторі
Перш ніж відправляти завдання на обладнання, ми перевіримо весь робочий процес на локальному зашумленому симуляторі. Ми використовуємо AerSimulator.from_backend(backend), щоб побудувати симулятор, який успадковує модель шуму та карту з'єднань вибраного тобою Backend, тому відповідь симулятора якісно схожа на те, що ми очікуємо від обладнання.
Крок 1: Відображення класичних входів на квантову задачу
Ми записуємо схему CHSH з одним параметром , який змінює базис вимірювання першого Qubit. Примітив Estimator спрощує аналіз: він повертає очікувані значення спостережуваних величин безпосередньо і може обчислювати параметризовану схему при багатьох значеннях параметра в одному виклику.
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")
Далі ми створюємо список з 21 значення фази від до , при яких обчислюватиметься параметризована схема (, , , ..., , ).
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]
Нарешті, ми визначаємо спостережувані величини. Перший Qubit вимірюється вздовж осей, повернутих на ; другий Qubit вимірюється в і . З цими виборами чотири кореляції CHSH відповідають операторам Паулі , , та :
# <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)]
)
Крок 2: Оптимізація задачі для виконання на квантовому обладнанні
Примітиви V2 приймають лише схеми та спостережувані величини, що відповідають інструкціям та з'єднанню, підтримуваним цільовою системою (схеми та спостережувані величини архітектури набору інструкцій, або ISA). Ми будуємо AerSimulator з Backend і транспілюємо відносно цілі симулятора, щоб той самий менеджер проходів використовувався наскрізно.
# 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")
Ми також перетворюємо спостережувані величини, щоб вони відповідали розміщенню Qubit транспільованої схеми, використовуючи SparsePauliOp.apply_layout.
isa_observable1 = observable1.apply_layout(layout=chsh_isa_circuit.layout)
isa_observable2 = observable2.apply_layout(layout=chsh_isa_circuit.layout)
Крок 3: Виконання за допомогою примітивів Qiskit
Запускаємо параметричне сканування з EstimatorV2 в режимі aer_sim. Метод run() Estimator приймає ітерований об'єкт PUB. Кожен PUB має формат (circuit, observables, parameter_values, precision). Ми передаємо обидві спостережувані величини разом, щоб вони поділяли одне й те саме параметричне сканування.
# 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()
Крок 4: Постобробка та повернення результату в бажаному класичному форматі
Estimator повертає очікувані значення для обох спостережуваних величин. Ми будуємо їх графік відносно разом з класичною межею () та межею Цірельсона (). Заштриховані сірі ділянки позначають проміжок між ними. Точки, що потрапляють у ці смуги, порушують нерівність CHSH.
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)",
)
Свідки CHSH симулятора вже перевищують класичну межу при декількох значеннях , навіть з моделлю шуму Backend. Піки трохи не досягають межі Цірельсона через змодельований шум пристрою. Після перевірки робочого процесу переходимо до реального обладнання.
Приклад на великому масштабі з реальним обладнанням
Тест CHSH — це по суті двокубітний експеримент, тому він не масштабується через збільшення однієї схеми. Натомість він масштабується завдяки виконанню багатьох тестів паралельно. Тут ми заповнюємо Backend якомога більшою кількістю роз'єднаних пар Белла, яку дозволяє його зв'язність (відповідність карти з'єднань), і запускаємо незалежну підсхему CHSH на кожній парі — все в одному завданні.
Це перетворює CHSH на загальнопристройний бенчмарк якості заплутаності: замість однієї вибраної вручну пари, ми тестуємо заплутаність на великій частині чіпа одночасно, в реалістичних умовах, де кожна пара стикається з перехресними перешкодами сусідів та помилками паралельних gate. Порушення нерівності на кожній парі одночасно підтверджує, що справжня заплутаність доступна скрізь на пристрої.
# -------------------------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()
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)

Бліді криві — це окремі пари Белла, а жирні криві — їх середнє значення по всьому пристрою. Кожна пара відстежує ту саму синусоїду, передбачену квантовою механікою, а розкид між блідими кривими відображає варіацію шуму від пари до пари. Скрізь, де крива входить у сірі смуги, вона перетинає класичну межу , і виведений підсумок підтверджує, що практично кожна пара порушує нерівність CHSH одночасно.
Піки не досягають межі Цірельсона через шум пристрою, але висновок однозначний: Backend підтримує справжню заплутаність по всьому чіпу одночасно, а не лише на одній вибраній вручну парі. Саме в цьому сенсі експеримент CHSH «масштабується»: не як одна більша схема, а як паралельний бенчмарк, що підтверджує заплутаність скрізь одночасно.
Наступні кроки
Якщо ця робота тебе зацікавила, можливо, тобі буде цікавий такий матеріал:
- Заплутаність в дії: урок курсу Джона Ватруса про стани Белла та гру CHSH.
- Початок роботи з примітивом Estimator: посібник з PUB та параметричними скануваннями.
- Бенчмаркінг у реальному часі для вибору Qubit: ще один спосіб охарактеризувати якість Qubit та заплутаності на пристрої.
- Довідник API
SparsePauliOp.