Saltar al contenido principal

Tu primer experimento cuántico

Introducción

En el siguiente video, Olivia Lanes te guía a través del contenido de esta lección. Alternativamente, puedes abrir el video de YouTube de esta lección en una ventana separada.

A estas alturas, ya has ejecutado tu primer Circuit cuántico y aprendido los fundamentos de la computación cuántica: cómo se representan los estados cuánticos, cómo actúan los Gates sobre esos estados, y cómo intervienen características cuánticas como la superposición y el entrelazamiento. Ahora es momento de poner todo esto en práctica y resolver tu primer problema en una computadora cuántica.

Exploraremos el panorama más amplio de los problemas adecuados para la computación cuántica en una lección posterior. Por ahora, nos centraremos en un problema del dominio de la simulación de la naturaleza: usar una computadora cuántica como un sustituto más limpio y controlable de un sistema cuántico natural. De hecho, esta fue la primera aplicación que Richard Feynman imaginó para las computadoras cuánticas en la década de 1980. Como dijo célebremente: "La naturaleza no es clásica, maldita sea, y si quieres hacer una simulación de la naturaleza, es mejor que la hagas cuántica..."

En esta lección, seguiremos ese principio para simular la interacción entre dos espines, que puedes imaginar como pequeños imanes. Dependiendo del signo de su interacción, pueden preferir alinearse y apuntar en la misma dirección, o anti-alinearse y apuntar en direcciones opuestas. Nos centraremos en el último caso porque a menudo conduce a un comportamiento más interesante — y más desafiante. Una vez que entendamos este pequeño sistema de dos qubits, mostraremos cómo las mismas ideas escalan, lo que permite a las computadoras cuánticas aprovechar su escalado exponencial al simular grandes sistemas de espines.

Dos imanes en interacción

Para este problema, usaremos dos qubits, uno para cada espín en nuestro modelo. Cada espín puede apuntar hacia arriba (estado del Qubit 0|0\rangle), hacia abajo (estado del Qubit 1|1\rangle), o en una superposición de los dos estados.

Si los espines tienen una interacción antiferromagnética, significa que quieren anti-alinearse, de modo que cuando uno apunta hacia arriba, el otro quiere apuntar hacia abajo, y viceversa.

Ahora supón que también hay un campo magnético que apunta de izquierda a derecha en nuestro sistema. Como este campo apunta a través de la dirección habitual arriba-abajo de los espines, se llama campo transversal. Este campo puede voltear los espines, lo que hace que la configuración de menor energía sea una superposición específica de arreglos de espines arriba-abajo en lugar de cualquier patrón de espín definido.

Podemos describir todos estos efectos usando un objeto matemático llamado Hamiltoniano. El Hamiltoniano nos dice la energía del sistema para un arreglo dado de espines:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

donde JJ es un coeficiente que controla la intensidad de la interacción entre espines y hxh_x es un coeficiente para la intensidad del campo magnético externo. Z1Z0Z_1 Z_0 premia o penaliza los espines dependiendo de si están alineados o anti-alineados, y X0X_0 y X1X_1 representan el efecto de volteo de espín del campo magnético.

En física, los sistemas tienden a asentarse en el estado con la menor energía posible, llamado el estado fundamental. Encontrar este estado de menor energía es un problema común, pero requiere técnicas de optimización que están más allá del alcance de esta lección.

En su lugar, haremos una pregunta más sencilla: Si preparamos los espines en un estado particular, ¿cuál es la energía de ese estado?

Para responder esto, haremos lo siguiente:

  1. Preparar los espines en un estado de nuestra elección
  2. Medir la energía de ese estado usando el Hamiltoniano anterior

Este es exactamente el tipo de cálculo que aparece dentro de algoritmos cuánticos más grandes, como los algoritmos variacionales, que puedes explorar en cursos posteriores.

Implementación en Qiskit

Antes de empezar a escribir código, necesitamos un poco de contexto. Cuando ejecutamos un Circuit cuántico, siempre terminamos midiendo los qubits. Pero hay dos tipos diferentes de preguntas que podríamos querer hacer sobre el resultado de esa medición: A veces, simplemente queremos saber cuál es el estado del Qubit. Otras veces, queremos saber, dado el estado cuántico, ¿cuál es el valor de una cantidad física, como la energía?

En Qiskit, estos dos tipos de preguntas se manejan con dos herramientas diferentes, llamadas primitivas.

Sampler responde el primer tipo de pregunta. Ejecuta el Circuit muchas veces y nos dice con qué frecuencia medimos cada resultado posible, como 00, 01, 10 o 11. El resultado es un histograma que muestra la probabilidad de cada resultado de medición.

Estimator responde el segundo tipo de pregunta. En lugar de darnos un histograma, combina muchas mediciones entre bastidores para calcular un único número, como la energía del estado según un Hamiltoniano que le proporcionamos.

Para ayudarte a entender cuándo y por qué usaríamos cada una de estas herramientas, recorreremos dos flujos de trabajo completos (llamados "patrones de Qiskit") aplicados al mismo sistema de dos qubits.

Flujo de trabajo de patrones de Qiskit

El flujo de trabajo de patrones de Qiskit es un marco general que usamos para resolver problemas cuánticos con Qiskit. Divide una tarea de computación cuántica en cuatro pasos:

  1. Mapear el problema a un modelo que pueda representarse mediante Circuits cuánticos
  2. Optimizar el Circuit para ejecutarlo en un backend específico
  3. Ejecutar el Circuit optimizado en el backend seleccionado
  4. Post-procesar los datos de medición en bruto

Experimento 1: Usa Sampler para medir el estado

Mapeo

En general, el paso de mapeo es donde determinamos cómo representar un problema del mundo real en términos de qubits, operadores y mediciones. En muchas aplicaciones, esta es la parte más complicada e involucrada del flujo de trabajo — incluso preguntas simples, como "¿qué representa cada Qubit?" no siempre tienen respuestas directas.

En este experimento, sin embargo, el mapeo es deliberadamente simple. Cada grado de libertad físico se mapea directamente a un único Qubit. Debido a esta correspondencia uno a uno, el paso de mapeo se reduce a elegir el estado cuántico que queremos preparar y escribir un Circuit que prepare y mida ese estado.

Aquí, prepararemos un estado de Bell entrelazado, similar al que creamos en la primera lección de este curso:

Ψ=12(1001)\vert\Psi\rangle = \frac{1}{\sqrt{2}}(\vert 10\rangle - \vert 01\rangle)
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Import Qiskit primitives
from qiskit import QuantumCircuit

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)

# Measure state
qc.measure_all()

# Draw circuit
qc.draw("mpl")

Output of the previous code cell

Optimización

Antes de ejecutar nuestro Circuit en una computadora cuántica (o en un simulador si has agotado tu tiempo gratuito en computadoras cuánticas reales para el mes), necesitamos prepararlo para la ejecución. Este paso se llama optimización. (Nota: este uso de la palabra "optimización" puede ser confuso. En computación cuántica, los problemas de optimización se refieren a una clase específica de problemas. Aquí, usamos "optimización" para describir un paso de preparación requerido por el que pasa todo Circuit cuántico antes de poder ejecutarse eficientemente en hardware.)

Durante la optimización:

  1. Elegimos el backend — ya sea una computadora cuántica real o un simulador.
  2. Asignamos los qubits de nuestro Circuit a qubits físicos en el dispositivo.
  3. Reescribimos el Circuit usando solo los Gates que la computadora cuántica puede realmente ejecutar.
  4. Opcionalmente implementamos técnicas de mitigación y supresión de errores para reducir los efectos del ruido.

En Qiskit, esto lo maneja automáticamente el transpilador. Una vez que eliges tu backend, el transpilador hace todo el trabajo para que tu Circuit esté listo para la ejecución, por lo que no tienes que ajustar manualmente los Gates ni las asignaciones de qubits. El transpilador también ofrece diferentes niveles de optimización, que pueden ayudar a reducir errores si es necesario. La optimización se realiza en etapas llamadas 'passes'. Por lo tanto, esta optimización será manejada por el pass_manager en el código a continuación. Para obtener más información sobre errores y mitigación de errores, consulta el curso de Olivia Lanes Quantum Computing in Practice.

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

## Load the Qiskit Runtime service
# QiskitRuntimeService.save_account(
# channel="ibm_quantum_platform",
# token="YOUR_TOKEN_HERE",
# overwrite=True,
# set_as_default=True,
# )
# service = QiskitRuntimeService(channel="ibm_quantum_platform")

# Or load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez
# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output of the previous code cell

Ejecución

¡Ahora estamos listos para ejecutar! Cargaremos Sampler y luego enviaremos el trabajo al backend.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

O, si estás usando un simulador, puedes descomentar y ejecutar esta celda en su lugar:

## Load the backend sampler
# from qiskit.primitives import BackendSamplerV2

## Load the Aer simulator and generate a noise model based on the currently-selected backend.
# from qiskit_aer import AerSimulator
# from qiskit_aer.noise import NoiseModel

# noise_model = NoiseModel.from_backend(backend)

## Define a simulator using Aer, and use it in Sampler.
# backend_sim = AerSimulator(noise_model=noise_model)
# sampler_sim = BackendSamplerV2(backend=backend_sim)

## Alternatively, load a fake backend with generic properties and define a simulator.
## backend_gen = GenericBackendV2(num_qubits=18)
## sampler_gen = BackendSamplerV2(backend=backend_gen)
job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

Post-procesamiento

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'10': 49, '01': 50, '11': 1}

Output of the previous code cell

Vemos que la mayoría de los conteos están en 01 o 10, lo que significa que cuando un Qubit se midió como 0, el otro fue 1, y viceversa. Esto es consistente con el estado de Bell Ψ\vert \Psi^- \rangle que preparamos.

Experimento 2: Usa Estimator para medir la energía

Ahora que hemos visto cómo muestrear un estado cuántico, usemos Estimator para calcular la energía de nuestro estado de Bell Ψ=12(0110)\vert \Psi^- \rangle = \frac{1}{\sqrt{2}}(\vert 01 \rangle - \vert 10 \rangle).

Mapeo

Como recordatorio, la energía del sistema está determinada por la interacción entre los espines (JJ) y el campo magnético externo (hxh_x) tal como lo captura el Hamiltoniano:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

Cada término en el Hamiltoniano nos dice cómo una determinada combinación de espines contribuye a la energía. En Qiskit, podemos representar estos términos como operadores de Pauli, que son simplemente etiquetas para acciones simples sobre qubits:

  • Z1Z0Z_1 Z_0 actúa con un ZZ sobre ambos qubits.
  • X0X_0 actúa con XX sobre el qubit 0.
  • X1X_1 actúa con XX sobre el qubit 1.

Un SparsePauliOp en Qiskit es una forma de almacenar una lista de estos operadores de Pauli junto con sus coeficientes numéricos. Estos operadores de Pauli son los observables que queremos que la computadora cuántica mida — las cantidades que nos dicen algo sobre el sistema. Usando Estimator, podemos calcular el valor promedio de cada observable en nuestro estado y combinarlos según los coeficientes del Hamiltoniano para obtener la energía total.

# Import Qiskit primitives
from qiskit.quantum_info import SparsePauliOp

# Parameters
J = 1.0 # antiferromagnetic coupling (J<0)
hx = -0.5 # transverse field strength

# 1. Define the Hamiltonian H = J Z1 Z2 + hx (X1 + X2)
obs = SparsePauliOp.from_list([("ZZ", J), ("XI", hx), ("IX", hx)])

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)
<qiskit.circuit.instructionset.InstructionSet at 0x1387ed630>

Ten en cuenta que omitimos la línea qc.measure_all() en nuestro código. Esto se debe a que con Estimator, no necesitamos especificar dónde medir en el Circuit. Solo le indicaremos qué observables queremos estimar, y Qiskit se encarga de las mediciones entre bastidores.

Optimización

El paso de optimización procede como antes, con la adición de asegurarnos de que nuestros observables también estén escritos de una manera que la computadora cuántica pueda entender.

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)

qc_isa.draw("mpl")

Output of the previous code cell

Ejecución

En el paso de ejecución, cargaremos Estimator y luego enviaremos el Circuit junto con la lista de observables que queremos estimar a la computadora cuántica.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
# Load the backend sampler

# noise_model = NoiseModel.from_backend(backend)

# Use Aer simulator in Estimator
# estimator_sim = BackendEstimatorV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# estimator_gen = BackendEstimatorV2(backend=backend_gen)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()

# Uncomment lines below to run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()

Post-procesamiento

Finalmente, en el paso de post-procesamiento, simplemente imprimimos la energía calculada entre bastidores por Estimator.

print(res[0].data.evs)
-0.9934112021453058

¡Esta es la energía de nuestro estado!

Conclusión

En esta lección, aprendimos cómo preparar un estado cuántico simple de dos qubits que representa dos espines en interacción. Vimos cómo usar Sampler para observar la distribución de resultados de medición y cómo usar Estimator para calcular la energía del estado según el Hamiltoniano. En el camino, vimos cómo el Hamiltoniano codifica las interacciones entre espines y los efectos de un campo externo, y cómo diferentes estados pueden tener diferentes energías.

Extensión a muchos espines

Hasta ahora, solo hemos visto dos espines, lo cual es suficientemente simple para analizar a mano. En sistemas físicos reales, como en un imán u otro material complejo, a menudo hay muchos espines en interacción. Cuando el número de espines aumenta, el Hamiltoniano se vuelve más complejo y encontrar el estado de menor energía se vuelve mucho más difícil. Aquí es donde las computadoras cuánticas pueden ayudar: preparando diferentes estados y estimando sus energías, podemos explorar configuraciones de baja energía de manera más eficiente que las computadoras clásicas para sistemas grandes.

Una extensión natural de este experimento sería aumentar el número de qubits para representar más espines y ajustar la manera en que los espines se preparan para intentar encontrar el estado de menor energía. Este enfoque es la esencia de los métodos variacionales, sobre los que puedes aprender en el curso Variational Quantum Algorithms.

También hay otros enfoques cuánticos para estudiar energías del estado fundamental que van más allá de las técnicas variacionales. Estos métodos no se cubren aquí, pero se presentan en el curso Quantum Diagonalization Algorithms si te interesa aprender más.

Objetivo de aprendizaje

Vuelve al principio del Experimento 2 e inténtalo con un estado de superposición diferente. ¿Puedes encontrar un estado con una energía aún más baja que el que usamos?

This translation based on the English version of 7 may 2026