Desigualdade CHSH
Estimativa de uso: Dois minutos em um processador Heron r3 (NOTA: Esta é apenas uma estimativa. Seu tempo de execução pode variar.)
Resultados de aprendizado
Após concluir este tutorial, você deve compreender as seguintes informações:
- Como construir um circuito CHSH de Bell parametrizado e medir os quatro valores esperados que compõem as testemunhas CHSH.
- Como calcular valores esperados de múltiplos observáveis em uma varredura de parâmetros em uma única chamada à primitiva
EstimatorV2. - Como validar um fluxo de trabalho quântico em um simulador local com ruído usando
AerSimulator.from_backendantes de enviar ao hardware. - Como escalar um experimento CHSH em um benchmark de emaranhamento para todo o dispositivo, executando muitos pares de Bell independentes em paralelo no hardware IBM Quantum®.
Pré-requisitos
Recomenda-se familiarizar-se com estes tópicos:
- Emaranhamento em Ação, uma lição de curso sobre estados de Bell e o jogo CHSH.
SparsePauliOpe a introdução às primitivas Qiskit.
Contexto
Neste tutorial, você executará um experimento em um computador quântico para demonstrar a violação da desigualdade CHSH com a primitiva Estimator.
A desigualdade CHSH, nomeada em homenagem aos autores Clauser, Horne, Shimony e Holt, é usada para provar experimentalmente o teorema de Bell (1969). O teorema afirma que teorias de variáveis ocultas locais não podem explicar algumas consequências do emaranhamento na mecânica quântica. Demonstrar uma violação da desigualdade CHSH mostra que a mecânica quântica é incompatível com teorias de variáveis ocultas locais, um experimento fundamental para nossa compreensão da mecânica quântica.
O Prêmio Nobel de Física de 2022 foi concedido a Alain Aspect, John Clauser e Anton Zeilinger, em parte por seu trabalho pioneiro em ciência da informação quântica e, em particular, por seus experimentos com fótons emaranhados demonstrando a violação das desigualdades de Bell.
Para este experimento, criaremos um par emaranhado no qual mediremos cada qubit em duas bases diferentes. Rotularemos as bases para o primeiro qubit como e e as bases para o segundo qubit como e . Isso nos permite calcular a quantidade CHSH :
Cada observável é ou . Claramente, um dos termos deve ser , e o outro deve ser . Portanto, . O valor médio de deve satisfazer a desigualdade:
Expandindo em termos de , , e resulta em:
Você pode definir outra quantidade CHSH :
o que leva a outra desigualdade:
Se a mecânica quântica pudesse ser descrita por teorias de variáveis ocultas locais, essas desigualdades seriam sempre válidas. Como demonstrado neste tutorial, elas podem ser violadas em um computador quântico, portanto a mecânica quântica não é compatível com teorias de variáveis ocultas locais.
Criaremos o par emaranhado preparando o estado de Bell . Usando a primitiva Estimator, obtemos os valores esperados e diretamente, sem reconstruí-los a partir de contagens brutas. Mediremos o segundo qubit nas bases e . O primeiro qubit também é medido em bases ortogonais, mas com um ângulo de rotação que variamos entre e . A primitiva Estimator avalia essa varredura de parâmetros em um único bloco unificado primitivo (PUB).
Requisitos
Antes de começar este tutorial, certifique-se de ter o seguinte instalado:
- Qiskit SDK v2.0 ou posterior, com suporte para visualização
- Qiskit Runtime v0.40 ou posterior (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 ou posterior (
pip install qiskit-aer)
Configuração
# 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'
Exemplo em pequena escala com simulador
Antes de enviar um trabalho ao hardware, validamos todo o fluxo de trabalho em um simulador local com ruído. Usamos AerSimulator.from_backend(backend) para construir um simulador que herda o modelo de ruído e o mapa de conectividade do backend selecionado, de modo que a resposta do simulador seja qualitativamente semelhante ao que esperamos do hardware.
Passo 1: Mapear entradas clássicas para um problema quântico
Escrevemos o circuito CHSH com um único parâmetro , que varia a base de medição do primeiro qubit. A primitiva Estimator simplifica a análise: ela retorna valores esperados de observáveis diretamente e pode avaliar um circuito parametrizado em muitos valores de parâmetros em uma única chamada.
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")
Em seguida, criamos uma lista de 21 valores de fase de a nos quais avaliar o circuito parametrizado (, , , ..., , ).
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 fim, definimos os observáveis. O primeiro qubit é medido ao longo de eixos rotacionados por ; o segundo qubit é medido em e . Com essas escolhas, os quatro correladores CHSH mapeiam para os operadores de Pauli , , e :
# <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)]
)
Passo 2: Otimizar o problema para execução em hardware quântico
As primitivas V2 aceitam apenas circuitos e observáveis que estejam em conformidade com as instruções e conectividade suportadas pelo sistema de destino (circuitos e observáveis de arquitetura de conjunto de instruções, ou ISA). Construímos o AerSimulator a partir do backend e transpilamos em relação ao alvo do simulador, de modo que o mesmo gerenciador de passagens seja exercitado de ponta a ponta.
# 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")
Também transformamos os observáveis para corresponder ao layout de qubits do 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)
Passo 3: Executar usando primitivas Qiskit
Execute a varredura de parâmetros com EstimatorV2 no modo aer_sim. O método run() do Estimator recebe um iterável de PUBs. Cada PUB tem o formato (circuit, observables, parameter_values, precision). Passamos ambos os observáveis juntos para que compartilhem a mesma varredura 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()
Passo 4: Pós-processar e retornar o resultado no formato clássico desejado
O Estimator retorna valores esperados para ambos os observáveis. Plotamos esses valores em função de , junto com o limite clássico () e o limite de Tsirelson (). As regiões cinzas sombreadas marcam a distância entre os dois. Pontos que caem dentro dessas faixas violam a desigualdade 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)",
)
As testemunhas CHSH do simulador já excedem o limite clássico de em vários valores de , mesmo com o modelo de ruído do backend. Os picos ficam ligeiramente abaixo do limite de Tsirelson devido ao ruído simulado do dispositivo. Com o fluxo de trabalho validado, passamos para o hardware real.
Exemplo em grande escala com hardware
Um teste CHSH é intrinsecamente um experimento de dois qubits, portanto ele não escala tornando um circuito maior. Em vez disso, ele escala executando muitos testes em paralelo. Aqui, cobrimos o backend com o máximo de pares de Bell disjuntos que sua conectividade permite (um emparelhamento do mapa de acoplamento) e executamos um sub-circuito CHSH independente em cada par, tudo em um único trabalho.
Isso transforma o CHSH em um benchmark de qualidade de emaranhamento para todo o dispositivo: em vez de um único par escolhido manualmente, testamos o emaranhamento em uma grande fração do chip de uma vez, sob condições realistas em que cada par compete com a interferência cruzada dos vizinhos e erros de gates paralelos. Violar a desigualdade em cada par simultaneamente certifica que emaranhamento genuíno está disponível em todo o 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)

As curvas suaves são os pares de Bell individuais e as curvas em negrito são suas médias ao longo do dispositivo. Cada par traça o mesmo seno previsto pela mecânica quântica, e a dispersão entre as curvas suaves reflete a variação no ruído de par a par. Onde quer que uma curva entre nas faixas cinzas, ela cruzou o limite clássico de , e o resumo impresso confirma que praticamente todos os pares violam a desigualdade CHSH ao mesmo tempo.
Os picos ficam abaixo do limite de Tsirelson devido ao ruído do dispositivo, mas a conclusão é inequívoca: o backend sustenta emaranhamento genuíno em todo o chip simultaneamente, não apenas em um único par escolhido manualmente. É neste sentido que o experimento CHSH "escala": não como um circuito maior, mas como um benchmark paralelo que certifica o emaranhamento em todo o lugar de uma vez.
Próximos passos
Se você achou este trabalho interessante, pode se interessar pelo seguinte material:
- Emaranhamento em Ação: uma lição de curso de John Watrous sobre estados de Bell e o jogo CHSH.
- Começar com a primitiva Estimator: um guia sobre PUBs e varreduras de parâmetros.
- Benchmarking em tempo real para seleção de qubits: outra forma de caracterizar a qualidade de qubits e emaranhamento em um dispositivo.
- Referência da API
SparsePauliOp.