Saltar al contenido principal

Desigualdad CHSH

Estimación de uso: Dos minutos en un procesador Heron r3 (NOTA: Esta es solo una estimación. Tu tiempo de ejecución puede variar.)

Resultados de aprendizaje

Después de completar este tutorial, podrás comprender la siguiente información:

  • Cómo construir un circuito CHSH de estado de Bell parametrizado y medir los cuatro valores esperados que forman los testigos CHSH.
  • Cómo calcular valores esperados de múltiples observables en un barrido de parámetros con una sola llamada a la primitiva EstimatorV2.
  • Cómo validar un flujo de trabajo cuántico en un simulador local con ruido usando AerSimulator.from_backend antes de enviarlo al hardware.
  • Cómo escalar un experimento CHSH a un benchmark de entrelazamiento de todo el dispositivo ejecutando muchos pares de Bell independientes en paralelo en hardware de IBM Quantum®.

Prerrequisitos

Se recomienda que te familiarices con estos temas:

Antecedentes

En este tutorial, ejecutarás un experimento en una computadora cuántica para demostrar la violación de la desigualdad CHSH con la primitiva Estimator.

La desigualdad CHSH, nombrada en honor a Clauser, Horne, Shimony y Holt, se utiliza para probar experimentalmente el teorema de Bell (1969). El teorema afirma que las teorías de variables ocultas locales no pueden dar cuenta de algunas consecuencias del entrelazamiento en la mecánica cuántica. Demostrar una violación de la desigualdad CHSH muestra que la mecánica cuántica es incompatible con las teorías de variables ocultas locales, un experimento que es fundamental para nuestra comprensión de la mecánica cuántica.

El Premio Nobel de Física 2022 fue otorgado a Alain Aspect, John Clauser y Anton Zeilinger en parte por su trabajo pionero en ciencia de la información cuántica y, en particular, por sus experimentos con fotones entrelazados que demostraron la violación de las desigualdades de Bell.

Para este experimento, crearemos un par entrelazado en el que mediremos cada qubit en dos bases diferentes. Etiquetaremos las bases del primer qubit como AA y aa, y las bases del segundo qubit como BB y bb. Esto nos permite calcular la cantidad CHSH S1S_1:

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

Cada observable es +1+1 o 1-1. Claramente, uno de los términos B±bB\pm b debe ser 00 y el otro debe ser ±2\pm 2. Por lo tanto, S1=±2S_1 = \pm 2. El valor promedio de S1S_1 debe satisfacer la desigualdad:

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

Expandir S1S_1 en términos de AA, aa, BB y bb resulta en:

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

Puedes definir otra cantidad CHSH S2S_2:

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

que lleva a otra desigualdad:

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

Si la mecánica cuántica pudiera describirse mediante teorías de variables ocultas locales, estas desigualdades siempre se cumplirían. Como se demuestra en este tutorial, pueden violarse en una computadora cuántica, por lo que la mecánica cuántica no es compatible con las teorías de variables ocultas locales.

Creamos el par entrelazado preparando el estado de Bell Φ+=00+112|\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}. Usando la primitiva Estimator, obtenemos directamente los valores esperados AB,Ab,aB\langle AB \rangle, \langle Ab \rangle, \langle aB \rangle y ab\langle ab \rangle, sin reconstruirlos a partir de conteos sin procesar. Medimos el segundo qubit en las bases ZZ y XX. El primer qubit también se mide en bases ortogonales, pero con un ángulo de rotación θ\theta que variamos entre 00 y 2π2\pi. La primitiva Estimator evalúa este barrido de parámetros en un único bloque unificado primitivo (PUB).

Requisitos

Antes de comenzar este tutorial, asegúrate de tener instalado lo siguiente:

  • Qiskit SDK v2.0 o posterior, con soporte de visualización
  • Qiskit Runtime v0.40 o posterior (pip install qiskit-ibm-runtime)
  • Qiskit Aer v0.17 o posterior (pip install qiskit-aer)

Configuración

# 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'

Ejemplo con simulador a pequeña escala

Antes de enviar un trabajo al hardware, validamos todo el flujo de trabajo en un simulador local con ruido. Usamos AerSimulator.from_backend(backend) para construir un simulador que hereda el modelo de ruido y el mapa de conectividad del backend seleccionado, de modo que la respuesta del simulador sea cualitativamente similar a la que esperamos del hardware.

Paso 1: Mapear entradas clásicas a un problema cuántico

Escribimos el circuito CHSH con un único parámetro θ\theta, que barre la base de medición del primer qubit. La primitiva Estimator simplifica el análisis: devuelve directamente los valores esperados de los observables y puede evaluar un circuito parametrizado en muchos valores de parámetros en una sola llamada.

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

A continuación, creamos una lista de 21 valores de fase de 00 a 2π2\pi en los que evaluar el circuito parametrizado (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]

Finalmente, definimos los observables. El primer qubit se mide a lo largo de ejes rotados por θ\theta; el segundo qubit se mide en ZZ y XX. Con estas elecciones, los cuatro correladores CHSH se mapean a los operadores de Pauli ZZZZ, ZXZX, XZXZ y 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)]
)

Paso 2: Optimizar el problema para la ejecución en hardware cuántico

Las primitivas V2 solo aceptan circuitos y observables que se ajustan a las instrucciones y conectividad compatibles con el sistema objetivo (circuitos y observables de arquitectura de conjunto de instrucciones, o ISA). Construimos el AerSimulator a partir del backend y transpilamos contra el objetivo del simulador, para que el mismo gestor de pases se ejercite de extremo a extremo.

# 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

También transformamos los observables para que coincidan con el diseño de qubits del circuito transpilado usando SparsePauliOp.apply_layout.

isa_observable1 = observable1.apply_layout(layout=chsh_isa_circuit.layout)
isa_observable2 = observable2.apply_layout(layout=chsh_isa_circuit.layout)

Paso 3: Ejecutar usando primitivas de Qiskit

Ejecutamos el barrido de parámetros con EstimatorV2 en modo aer_sim. El método run() del Estimator toma un iterable de PUBs. Cada PUB tiene el formato (circuit, observables, parameter_values, precision). Pasamos ambos observables juntos para que compartan el mismo barrido de parámetros.

# 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()

Paso 4: Post-procesar y devolver el resultado en el formato clásico deseado

El Estimator devuelve valores esperados para ambos observables. Los graficamos contra θ\theta junto con el límite clásico (±2\pm 2) y el límite de Tsirelson (±22\pm 2\sqrt{2}). Las regiones sombreadas en gris marcan la brecha entre los dos. Los puntos que caen dentro de estas bandas violan la desigualdad 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

Los testigos CHSH del simulador ya superan el límite clásico de ±2\pm 2 en varios valores de θ\theta, incluso con el modelo de ruido del backend. Los picos se quedan ligeramente por debajo del límite de Tsirelson ±22\pm 2\sqrt{2} debido al ruido simulado del dispositivo. Con el flujo de trabajo validado, pasamos al hardware real.

Ejemplo de hardware a gran escala

Una prueba CHSH es intrínsecamente un experimento de dos qubits, por lo que no escala haciendo un circuito más grande. En cambio, escala ejecutando muchas pruebas en paralelo. Aquí cubrimos el backend con tantos pares de Bell disjuntos como permita su conectividad (un emparejamiento del mapa de acoplamiento) y ejecutamos un sub-circuito CHSH independiente en cada par, todo en un único trabajo.

Esto convierte al CHSH en un benchmark de calidad de entrelazamiento de todo el dispositivo: en lugar de un único par seleccionado a mano, probamos el entrelazamiento en una gran fracción del chip a la vez, bajo condiciones realistas donde cada par compite con la diafonía de sus vecinos y los errores de puertas en paralelo. Violar la desigualdad en cada par simultáneamente certifica que el entrelazamiento genuino está disponible en todo el dispositivo.

# -------------------------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

Las curvas tenues corresponden a los pares de Bell individuales y las curvas en negrita son su media en todo el dispositivo. Cada par traza el mismo seno predicho por la mecánica cuántica, y la dispersión entre las curvas tenues refleja la variación en el ruido de par a par. Siempre que una curva entra en las bandas grises, ha cruzado el límite clásico de ±2\pm 2, y el resumen impreso confirma que esencialmente todos los pares violan la desigualdad CHSH al mismo tiempo.

Los picos no alcanzan el límite de Tsirelson ±22\pm 2\sqrt{2} debido al ruido del dispositivo, pero la conclusión es inequívoca: el backend mantiene entrelazamiento genuino en todo el chip simultáneamente, no solo en un único par seleccionado a mano. Este es el sentido en el que el experimento CHSH "escala": no como un circuito más grande, sino como un benchmark paralelo que certifica el entrelazamiento en todas partes a la vez.

Próximos pasos

Recomendaciones

Si este trabajo te resultó interesante, puede que te interese el siguiente material: