Skip to main content
IBM Quantum Platform

Desigualdad CHSH

Estimación de tiempo de ejecución: Dos minutos en un procesador Heron r3 (NOTA: Se trata únicamente de una estimación. (El tiempo de ejecución puede variar.)


Resultados del aprendizaje

Una vez completado este tutorial, habrás adquirido los siguientes conocimientos:

  • Cómo construir un circuito CHSH de estado de Bell parametrizado y medir los cuatro valores esperados que conforman los testigos CHSH.
  • Cómo calcular los valores esperados de varias variables observables en un barrido de parámetros mediante una única llamada a la EstimatorV2 primitiva.
  • Cómo validar un flujo de trabajo cuántico en un simulador local con AerSimulator.from_backend ruido antes de ejecutarlo en el hardware.
  • Cómo ampliar un experimento CHSH para convertirlo en una prueba de referencia del entrelazamiento a escala de dispositivo mediante la ejecución en paralelo de numerosos pares de Bell independientes en un hardwar IBM Quantum®.

Requisitos previos

Se recomienda que te familiarices con los siguientes temas:


En segundo plano

En este tutorial, realizarás un experimento en un ordenador cuántico para demostrar la violación de la desigualdad CHSH con la primitiva Estimador.

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

El Premio Nobel de Física de 2022 fue otorgado a Alain Aspect, John Clauser y Anton Zeilinger, en parte por su trabajo pionero en el campo de la 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. Denominaremos las bases del primer qubit AA y aa, y las del segundo qubit BB y bb. Esto nos permite calcular la magnitud CHSH S1S_1 :

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

Cada observable es o bien +1+1 o bien 1-1. Es evidente que 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 medio de S1S_1 debe satisfacer la desigualdad:

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

Al desarrollar « S1S_1 » en términos de « AA », « aa », « BB » y « bb », se obtiene:

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

Puede definir otra cantidad CHSH S2S_2 :

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

lo que nos 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 se cumplirían siempre. Como se ha demostrado en este tutorial, estas leyes pueden incumplirse en un ordenador cuántico, 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}}. Mediante la primitiva «Estimador», obtenemos directamente los valores esperados AB,Ab,aB\langle AB \rangle, \langle Ab \rangle, \langle aB \rangle y ab\langle ab \rangle, sin necesidad de reconstruirlos a partir de los recuentos brutos. 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 esta variación del parámetro en un único bloque primitivo unificado ( PUB ).


Requisitos

Antes de empezar este tutorial, asegúrate de tener instalados los siguientes elementos:

  • Qiskit SDK v2.0 o posterior, con soporte para 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

# 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

Output:

'ibm_pittsburgh'

Ejemplo de simulador a pequeña escala

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

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

Escribimos el circuito CHSH con un único parámetro θ\theta, que recorre la base de medición del primer qubit. La Estimator primitiva simplifica el análisis: devuelve directamente los valores esperados de las magnitudes observables y permite evaluar un circuito parametrizado con múltiples valores de los 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:

Output of the previous code cell

A continuación, creamos una lista de 21 valores de fase, desde 00 hasta 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]

Por último, definimos las magnitudes observables. El primer qubit se mide a lo largo de ejes girados en un ángulo de θ\theta; el segundo qubit se mide en ZZ y XX. Con estas elecciones, los cuatro correladores CHSH se corresponden con 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

V2 Las primitivas solo admiten circuitos y observables que se ajusten a las instrucciones y a la conectividad que admite el sistema de destino (arquitectura de conjunto de instrucciones, o ISA, circuitos y observables). Compilamos el AerSimulator desde el backend y lo transpilamos para el destino del simulador, de modo que se ejecute el mismo gestor de pasadas de principio a fin.

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

Output of the previous code cell

Además, transformamos los observables para que se ajusten a la disposición de los qubits del circuito transpilado utilizando 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 utilizando Qiskit primitives

Ejecuta el barrido de parámetros en aer_sim``EstimatorV2 modo. El método Estimator run() admite un iterable de PUB. Cada archivo « PUB » tiene el formato (circuit, observables, parameter_values, precision). Pasamos ambos observables a la vez 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: Procesamiento posterior y devolución del resultado en el formato clásico deseado

El estimador devuelve los valores esperados para ambas variables observables. Las representamos gráficamente en θ\theta junto con el límite clásico ( ±2\pm 2 ) y el límite de Tsirelson ( ±22\pm 2\sqrt{2} ). Las zonas sombreadas en gris marcan la diferencia entre ambos. Los puntos que se encuentran dentro de estas bandas incumplen la desigualdad de 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:

Output of the previous code cell

Los valores de CHSH observados en el 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. Una vez validado el flujo de trabajo, pasamos al hardware propiamente dicho.


Ejemplo de hardware a gran escala

Una prueba CHSH es, por naturaleza, un experimento de dos qubits, por lo que no se puede ampliar simplemente aumentando el tamaño de un circuito. En cambio, se adapta ejecutando muchas pruebas en paralelo. Aquí dividimos el backend en tantos pares de Bell disjuntos como permita su conectividad (una correspondencia del mapa de acoplamiento) y ejecutamos un subcircuito CHSH independiente en cada par, todo ello en un único trabajo.

Esto convierte al CHSH en un punto de referencia de la calidad del entrelazamiento a escala de todo el dispositivo : en lugar de un único par seleccionado manualmente, evaluamos el entrelazamiento en una gran parte del chip de una sola vez, en condiciones realistas en las que cada par se ve afectado por la diafonía de sus vecinos y por los errores de las puertas paralelas. El hecho de infringir la desigualdad en todos los pares al mismo tiempo confirma que existe un entrelazamiento genuino 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()

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)
Output of the previous code cell

Las curvas tenues representan los pares de Bell individuales y las curvas en negrita, su media en todo el dispositivo. Cada par describe la misma sinusoide prevista por la mecánica cuántica, y la dispersión entre las tenues curvas refleja la variación del ruido de un par a otro. Siempre que una curva entra en las bandas grises, significa que ha traspasado el límite clásico de ±2\pm 2, y el resumen impreso confirma que, en esencia, todos los pares incumplen la desigualdad de 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 un entrelazamiento auténtico en todo el chip de forma simultánea, y no solo en un único par seleccionado manualmente. En este sentido es en el que el experimento del CHSH «se amplía»: no como un circuito más grande, sino como un punto de referencia paralelo que certifica el entrelazamiento en todas partes a la vez.


Próximos pasos

Recomendaciones

Si este trabajo te ha parecido interesante, quizá te interese el siguiente material:

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.