Concepts Intermediate Free 50/53 in series 30 min read

Bell Inequalities and the CHSH Test: How We Know Entanglement Is Real

Derive the CHSH inequality, understand why classical correlations cannot violate it, and verify that quantum entanglement does — with working Qiskit code that reproduces Bell test results.

What you'll learn

  • entanglement
  • bell inequalities
  • CHSH
  • quantum nonlocality
  • measurement

Prerequisites

  • entanglement
  • quantum measurement
  • basic probability

Bell inequalities are the experimental test that separates quantum entanglement from every classical explanation. This tutorial derives the CHSH version, explains what violating it means, and shows how to reproduce a Bell test on a quantum simulator.

The problem Bell solved

When Einstein, Podolsky, and Rosen (EPR) published their challenge to quantum mechanics in 1935, they argued that the theory was incomplete. Their intuition: if measuring one particle instantly determines the outcome for a distant particle, surely both outcomes were predetermined — just unknown. This is the hidden variable hypothesis.

John Bell proved in 1964 that this hypothesis has testable consequences. If outcomes are predetermined by hidden variables, then certain statistical correlations between measurements are bounded. Quantum mechanics predicts those same correlations exceed the bound. Only one of them can be right.

The answer turns out to be quantum mechanics.

Setting up the CHSH test

Alice and Bob share a pair of entangled qubits. Alice measures her qubit in one of two bases, labelled A₀ or A₁. Bob measures in one of two bases, B₀ or B₁. Each measurement outcome is +1 or −1.

Define the CHSH quantity:

S = ⟨A₀B₀⟩ + ⟨A₀B₁⟩ + ⟨A₁B₀⟩ − ⟨A₁B₁⟩

where ⟨AᵢBⱼ⟩ is the expectation value of the product of Alice’s and Bob’s outcomes when they use settings i and j.

The classical bound

Claim: For any hidden variable model (any predetermined ±1 outcomes), |S| ≤ 2.

Proof: At any given run, Alice’s qubit has predetermined values a₀, a₁ ∈ {+1, −1} for each setting, and Bob’s has b₀, b₁ ∈ {+1, −1}. Consider:

a₀b₀ + a₀b₁ + a₁b₀ − a₁b₁
= a₀(b₀ + b₁) + a₁(b₀ − b₁)

Since b₀, b₁ ∈ {+1, −1}, either b₀+b₁ = ±2 and b₀−b₁ = 0, or b₀+b₁ = 0 and b₀−b₁ = ±2. In either case, one term is ±2 and the other is 0:

|a₀(b₀ + b₁) + a₁(b₀ − b₁)| ≤ 2

Averaging over many runs (with any probability distribution over hidden variables) preserves this bound. Therefore |S| ≤ 2 for any classical model.

The quantum prediction

Use the Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2 and measurement settings:

A₀: measure in Z basis        (angle 0)
A₁: measure in X basis        (angle π/2)
B₀: measure at angle π/4
B₁: measure at angle −π/4

For a Bell state, the correlation between measurements at angles θ_A and θ_B is:

⟨AᵢBⱼ⟩ = cos(2(θ_A − θ_B))

Substituting our angles:

⟨A₀B₀⟩ = cos(2(0 − π/4))   = cos(−π/2) = 1/√2
⟨A₀B₁⟩ = cos(2(0 + π/4))   = cos(π/2)  = 1/√2
⟨A₁B₀⟩ = cos(2(π/2 − π/4)) = cos(π/2)  = 1/√2
⟨A₁B₁⟩ = cos(2(π/2 + π/4)) = cos(3π/2) = −1/√2

Therefore:

S = 1/√2 + 1/√2 + 1/√2 − (−1/√2) = 4/√2 = 2√2 ≈ 2.828

This exceeds the classical bound of 2 by a factor of √2. This is the Tsirelson bound — the maximum possible quantum violation.

Running a Bell test in Qiskit

from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
import numpy as np

def measure_correlation(theta_a: float, theta_b: float, shots: int = 100_000) -> float:
    """
    Measure correlation ⟨AB⟩ for a Bell state |Φ+⟩
    where Alice measures at angle theta_a and Bob at theta_b.
    """
    qc = QuantumCircuit(2)
    # Prepare Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2
    qc.h(0)
    qc.cx(0, 1)
    # Rotate Alice's measurement basis
    qc.ry(-2 * theta_a, 0)   # Ry rotation to measure in rotated basis
    # Rotate Bob's measurement basis
    qc.ry(-2 * theta_b, 1)
    # Measure in Z basis (outcome 0 → +1, outcome 1 → −1)
    qc.measure_all()

    from qiskit_aer import AerSimulator
    result = AerSimulator().run(qc, shots=shots).result()
    counts = result.get_counts()

    total = 0
    for bitstring, count in counts.items():
        # bitstring is "bob alice" (Qiskit reverses bit order)
        alice_bit = int(bitstring[-1])
        bob_bit = int(bitstring[0])
        alice_outcome = 1 - 2 * alice_bit   # 0 → +1, 1 → −1
        bob_outcome = 1 - 2 * bob_bit
        total += alice_outcome * bob_outcome * count

    return total / shots

# CHSH measurement settings
SHOTS = 200_000
a0, a1 = 0, np.pi / 4        # Alice: 0° and 45°
b0, b1 = np.pi / 8, -np.pi / 8  # Bob: 22.5° and -22.5°

corr_00 = measure_correlation(a0, b0, SHOTS)
corr_01 = measure_correlation(a0, b1, SHOTS)
corr_10 = measure_correlation(a1, b0, SHOTS)
corr_11 = measure_correlation(a1, b1, SHOTS)

S = corr_00 + corr_01 + corr_10 - corr_11

print(f"⟨A₀B₀⟩ = {corr_00:.4f}")
print(f"⟨A₀B₁⟩ = {corr_01:.4f}")
print(f"⟨A₁B₀⟩ = {corr_10:.4f}")
print(f"⟨A₁B₁⟩ = {corr_11:.4f}")
print(f"S = {S:.4f}")
print(f"Classical bound: 2.000")
print(f"Tsirelson bound: {2*np.sqrt(2):.4f}")
print(f"Violation: {'YES' if abs(S) > 2 else 'NO'}")

Expected output:

⟨A₀B₀⟩ ≈  0.7071
⟨A₀B₁⟩ ≈  0.7071
⟨A₁B₀⟩ ≈  0.7071
⟨A₁B₁⟩ ≈ -0.7071
S ≈ 2.8284
Classical bound: 2.000
Tsirelson bound: 2.8284
Violation: YES

The simulation reaches the Tsirelson bound because it has no noise. On real hardware the value will be lower (typically 2.5–2.7 on current IBM systems due to gate errors and readout errors).

What the violation tells us

A CHSH value S > 2 proves that no local hidden variable theory can explain the measurement outcomes. Specifically, it rules out any model where:

  1. Locality: Alice’s measurement outcome does not depend on Bob’s choice of measurement setting, and vice versa.
  2. Realism: Each particle has definite predetermined outcomes for all possible measurements.

Both conditions together — local realism — are inconsistent with S > 2. One of them (or both) must be wrong.

The loophole-free Bell test experiments of 2015 (Delft, NIST, Vienna simultaneously) closed the major escape routes:

  • Locality loophole: Closed by separating Alice and Bob far enough that no light-speed signal could connect the events.
  • Detection loophole: Closed by using high-efficiency detectors (>80%) so you cannot selectively post-select favourable runs.
  • Freedom-of-choice loophole: Closed by using random number generators to choose measurement settings.

The verdict: local realism is experimentally refuted. The universe is genuinely non-local in the sense that entangled particles do not have predetermined outcomes.

What the violation does NOT tell us

  • It does not mean Alice and Bob can send information faster than light. The outcomes are individually random; the non-local correlation only becomes visible after classical communication.
  • It does not prove the many-worlds or Copenhagen interpretation is correct. Bell tests rule out local hidden variable theories but not all alternatives.
  • It does not mean quantum mechanics is “spooky.” The correlation is a consequence of the structure of quantum states, not of any physical influence travelling between particles.

The CHSH game interpretation

Bell inequalities have an elegant game-theoretic interpretation. Alice and Bob play a cooperative game:

  • Referee sends them bits x, y ∈ {0, 1} independently at random.
  • Alice responds with a ∈ {0, 1}, Bob with b ∈ {0, 1}.
  • They win if a XOR b = x AND y (i.e., they output different bits exactly when both receive 1).

Classical winning probability: The best classical strategy wins at most 75% of the time (3/4). This is because when x=y=1 they need a≠b, but for all other inputs they need a=b, and there is no way to satisfy all four cases simultaneously.

Quantum winning probability: Sharing a Bell state and using optimal measurement settings, they win cos²(π/8) ≈ 85.4% of the time. This exceeds the classical limit.

The quantum advantage in the CHSH game directly corresponds to violating the Bell inequality. Both are manifestations of the same non-local correlations.

Running on real IBM hardware

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=2)

# Build the circuit as before, then transpile
qc = build_bell_test_circuit(a0, b0)  # use your settings
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
transpiled = pm.run(qc)

sampler = SamplerV2(backend)
job = sampler.run([transpiled], shots=10_000)
result = job.result()
# Extract counts and compute correlation as above

Real hardware typically gives S ≈ 2.4–2.6, below the ideal 2√2 ≈ 2.828 but well above the classical bound of 2. The gap from the ideal value reflects gate error rates and readout assignment errors on current NISQ hardware.

Was this tutorial helpful?