Перейти до основного вмісту

Нерівність CHSH

Оцінка використання: Дві хвилини на процесорі Heron r3 (ПРИМІТКА: Це лише оцінка. Твій час виконання може відрізнятися.)

Результати навчання

Після завершення цього посібника ти зможеш розуміти таке:

  • Як побудувати параметризовану схему CHSH зі станом Белла та виміряти чотири очікувані значення, що складають свідків CHSH.
  • Як обчислити очікувані значення кількох спостережуваних величин при параметричному скануванні в одному виклику до примітиву EstimatorV2.
  • Як перевірити квантовий робочий процес на локальному зашумленому симуляторі з AerSimulator.from_backend перед відправкою на обладнання.
  • Як масштабувати експеримент CHSH до загальнопристройного бенчмарку заплутаності, запускаючи багато незалежних пар Белла паралельно на обладнанні IBM Quantum®.

Передумови

Рекомендується ознайомитися з такими темами:

Загальна інформація

У цьому посібнику ти виконаєш експеримент на квантовому комп'ютері, щоб продемонструвати порушення нерівності CHSH за допомогою примітиву Estimator.

Нерівність CHSH, названа на честь авторів Клаузера, Хорна, Шимоні та Холта, використовується для експериментального доведення теореми Белла (1969). Ця теорема стверджує, що теорії локальних прихованих змінних не можуть пояснити деякі наслідки заплутаності в квантовій механіці. Порушення нерівності CHSH демонструє, що квантова механіка несумісна з теоріями локальних прихованих змінних — це фундаментальний для нашого розуміння квантової механіки експеримент.

Нобелівську премію з фізики 2022 року було присуджено Алену Аспе, Джону Клаузеру та Антону Цайлінгеру частково за їхню піонерську роботу в квантовій інформатиці, зокрема за експерименти з заплутаними фотонами, що демонструють порушення нерівностей Белла.

Для цього експерименту ми створимо заплутану пару, де будемо вимірювати кожен Qubit у двох різних базисах. Ми позначимо базиси для першого Qubit AA і aa, а базиси для другого Qubit BB і bb. Це дозволяє нам обчислити величину CHSH S1S_1:

S1=A(Bb)+a(B+b).S_1 = A(B-b) + a(B+b).

Кожна спостережувана величина дорівнює або +1+1, або 1-1. Очевидно, один з членів B±bB\pm b повинен дорівнювати 00, а інший має бути ±2\pm 2. Отже, S1=±2S_1 = \pm 2. Середнє значення S1S_1 має задовольняти нерівність:

S12.|\langle S_1 \rangle|\leq 2.

Розкладання S1S_1 через AA, aa, BB і bb дає:

S1=ABAb+aB+ab2.|\langle S_1 \rangle| = |\langle AB \rangle - \langle Ab \rangle + \langle aB \rangle + \langle ab \rangle| \leq 2.

Ти можеш визначити іншу величину CHSH S2S_2:

S2=A(B+b)a(Bb),S_2 = A(B+b) - a(B-b),

що призводить до іншої нерівності:

S2=AB+AbaB+ab2.|\langle S_2 \rangle| = |\langle AB \rangle + \langle Ab \rangle - \langle aB \rangle + \langle ab \rangle| \leq 2.

Якщо квантову механіку можна описати теоріями локальних прихованих змінних, ці нерівності виконувалися б завжди. Як демонструється в цьому посібнику, вони можуть бути порушені на квантовому комп'ютері, тобто квантова механіка несумісна з теоріями локальних прихованих змінних.

Ми створимо заплутану пару, підготувавши стан Белла Φ+=00+112|\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}. Використовуючи примітив Estimator, ми отримаємо очікувані значення AB,Ab,aB\langle AB \rangle, \langle Ab \rangle, \langle aB \rangle та ab\langle ab \rangle безпосередньо, без реконструкції з необроблених підрахунків. Ми вимірюємо другий Qubit у базисах ZZ і XX. Перший Qubit також вимірюється в ортогональних базисах, але з кутом обертання θ\theta, який ми змінюємо від 00 до 2π2\pi. Примітив 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 з одним параметром θ\theta, який змінює базис вимірювання першого 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")

Output of the previous code cell

Далі ми створюємо список з 21 значення фази від 00 до 2π2\pi, при яких обчислюватиметься параметризована схема (00, 0.1π0.1\pi, 0.2π0.2\pi, ..., 1.9π1.9\pi, 2π2\pi).

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 вимірюється вздовж осей, повернутих на θ\theta; другий Qubit вимірюється в ZZ і XX. З цими виборами чотири кореляції CHSH відповідають операторам Паулі ZZZZ, ZXZX, XZXZ та XXXX:

S1=ZZZX+XZ+XX,\langle S_1 \rangle = \langle ZZ \rangle - \langle ZX \rangle + \langle XZ \rangle + \langle XX \rangle, S2=ZZ+ZXXZ+XX.\langle S_2 \rangle = \langle ZZ \rangle + \langle ZX \rangle - \langle XZ \rangle + \langle XX \rangle.
# <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")

Output of the previous code cell

Ми також перетворюємо спостережувані величини, щоб вони відповідали розміщенню 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 повертає очікувані значення для обох спостережуваних величин. Ми будуємо їх графік відносно θ\theta разом з класичною межею (±2\pm 2) та межею Цірельсона (±22\pm 2\sqrt{2}). Заштриховані сірі ділянки позначають проміжок між ними. Точки, що потрапляють у ці смуги, порушують нерівність 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)",
)

Output of the previous code cell

Свідки CHSH симулятора вже перевищують класичну межу ±2\pm 2 при декількох значеннях θ\theta, навіть з моделлю шуму Backend. Піки трохи не досягають межі Цірельсона ±22\pm 2\sqrt{2} через змодельований шум пристрою. Після перевірки робочого процесу переходимо до реального обладнання.

Приклад на великому масштабі з реальним обладнанням

Тест 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)

Output of the previous code cell

Бліді криві — це окремі пари Белла, а жирні криві — їх середнє значення по всьому пристрою. Кожна пара відстежує ту саму синусоїду, передбачену квантовою механікою, а розкид між блідими кривими відображає варіацію шуму від пари до пари. Скрізь, де крива входить у сірі смуги, вона перетинає класичну межу ±2\pm 2, і виведений підсумок підтверджує, що практично кожна пара порушує нерівність CHSH одночасно.

Піки не досягають межі Цірельсона ±22\pm 2\sqrt{2} через шум пристрою, але висновок однозначний: Backend підтримує справжню заплутаність по всьому чіпу одночасно, а не лише на одній вибраній вручну парі. Саме в цьому сенсі експеримент CHSH «масштабується»: не як одна більша схема, а як паралельний бенчмарк, що підтверджує заплутаність скрізь одночасно.

Наступні кроки

Рекомендації

Якщо ця робота тебе зацікавила, можливо, тобі буде цікавий такий матеріал: