Skip to main content
IBM Quantum Platform

Ejecute su primer circuito en hardware

  • El código de esta página se ha desarrollado siguiendo los siguientes requisitos. Recomendamos utilizar estas versiones o posteriores.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    

Este ejemplo consta de dos partes. Primero crearás un sencillo programa cuántico «Hola mundo» y lo ejecutarás en una unidad de procesamiento cuántico (QPU). Dado que la investigación cuántica real requiere programas mucho más robustos, en la segunda sección ( Escalado a un gran número de qubits ), ampliarás el programa simple hasta alcanzar un nivel de utilidad.


Instalar y autenticar

  1. Si aún no ha instalado Qiskit, consulte las instrucciones de la guía de inicio rápido.

    • Instalar Qiskit Runtime para ejecutar trabajos en hardware cuántico:

      pip install qiskit-ibm-runtime
    • Configure un entorno para ejecutar cuadernos Jupyter localmente:

      pip install jupyter
  2. Configure su autenticación para acceder al hardware cuántico a través del plan gratuito Open Plan.

    (Si ha recibido una invitación por correo electrónico para unirse a una cuenta, siga los pasos para usuarios invitados )

    • Vaya a IBM Quantum Platform para iniciar sesión o crear una cuenta.

      Importante

      Si se conecta a través de un servidor proxy, debe utilizar Qiskit Runtime v0.44.0 o una versión posterior.

    • Genere su clave API (también denominada token API) en el panel de control y, a continuación, cópiela en una ubicación segura.

    • Vaya a la página Instancias y busque la instancia que desea utilizar. Pase el cursor sobre su CRN y haga clic para copiarlo.

    • Guarda tus credenciales localmente con este código:

      from qiskit_ibm_runtime import QiskitRuntimeService
      
      QiskitRuntimeService.save_account(
          # For `token`, use the 44-character API_KEY you created
          # and saved from the IBM Quantum Platform Home dashboard
          token="<your-api-key>",
          instance="<CRN>", # Optional
      )
  3. Ahora puede utilizar este Python código cada vez que desee autenticarse en el Qiskit Runtime servicio:

    from qiskit_ibm_runtime import QiskitRuntimeService
    
    # Run every time you need the service
    service = QiskitRuntimeService()
¿No utiliza un entorno de Python confianza?

Si utiliza un ordenador público u otro entorno no seguro, siga las instrucciones de autenticación manual para mantener seguras sus credenciales de autenticación.


Crear y ejecutar un programa cuántico sencillo

Los cuatro pasos para escribir un programa cuántico utilizando patrones Qiskit son:

  1. Asigne el problema a un formato cuántico nativo.

  2. Optimizar los circuitos y los operadores.

  3. Ejecutar utilizando una función primitiva cuántica.

  4. Analice los resultados.

Paso 1. Asignar el problema a un formato cuántico nativo

En un programa cuántico, los circuitos cuánticos son el formato nativo en el que se representan las instrucciones cuánticas, y los operadores representan los observables que se van a medir. Al crear un circuito, normalmente se crea un nuevo QuantumCircuit objeto y luego se le añaden instrucciones en secuencia.

La siguiente celda de código crea un circuito que produce un estado Bell, que es un estado en el que dos qubits están completamente entrelazados entre sí.

Nota: orden de los bits

El Qiskit SDK utiliza la numeración de bits LSb 0, donde el nthn^{th} dígito tiene un valor 1n1 \ll n o 2n2^n. Para obtener más detalles, consulte el Qiskit SDK tema Ordenación de bits en el.

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorOptions
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from matplotlib import pyplot as plt
# Uncomment the next line if you want to use a simulator:
# from qiskit_ibm_runtime.fake_provider import FakeBelemV2


# Create a new circuit with two qubits
qc = QuantumCircuit(2)

# Add a Hadamard gate to qubit 0
qc.h(0)

# Perform a controlled-X gate on qubit 1, controlled by qubit 0
qc.cx(0, 1)

# Return a drawing of the circuit using MatPlotLib ("mpl").
# These guides are written by using Jupyter notebooks, which
# display the output of the last line of each cell.
# If you're running this in a script, use `print(qc.draw())` to
# print a text drawing.
qc.draw("mpl")

Output:

Output of the previous code cell

Consulte QuantumCircuit la documentación para ver todas las operaciones disponibles.

Al crear circuitos cuánticos, también hay que tener en cuenta qué tipo de datos se desea obtener tras la ejecución. Qiskit ofrece dos formas de obtener datos: puedes obtener resultados de muestreo para un conjunto de qubits que elijas medir, o puedes obtener el valor esperado de una observable. Prepara tu carga de trabajo para medir tu circuito de una de estas dos formas con Qiskit primitives (se explica con detalle en el paso 3 ).

Este ejemplo mide los valores esperados utilizando el qiskit.quantum_info submódulo, que se especifica mediante operadores (objetos matemáticos utilizados para representar una acción o proceso que cambia un estado cuántico). La siguiente celda de código crea seis operadores de Pauli de dos qubits: IZ, IX, ZI XI, ZZ, y XX.

# Set up six different observables.

observables_labels = ["IZ", "IX", "ZI", "XI", "ZZ", "XX"]
observables = [SparsePauliOp(label) for label in observables_labels]
Notación del operador

Aquí, algo como el ZZ operador es una abreviatura del producto tensorial ZZZ\otimes Z, lo que significa medir Z en el qubit 1 y Z en el qubit 0 juntos, y obtener información sobre la correlación entre el qubit 1 y el qubit 0. Los valores esperados como este también se suelen escribir como Z1Z0\langle Z_1 Z_0 \rangle.

Si el estado está entrelazado, entonces la medición de Z1Z0\langle Z_1 Z_0 \rangle debería ser diferente de la medición de I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle. Para el estado entrelazado específico creado por nuestro circuito descrito anteriormente, la medición de Z1Z0\langle Z_1 Z_0 \rangle debería ser 1 y la medición de I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle debería ser cero.

Paso 2. Optimizar los circuitos y los operadores

Al ejecutar circuitos en un dispositivo, es importante optimizar el conjunto de instrucciones que contiene el circuito y minimizar la profundidad total (aproximadamente el número de instrucciones) del circuito. Esto garantiza que obtenga los mejores resultados posibles al reducir los efectos del error y el ruido. Además, las instrucciones del circuito deben ajustarse a la arquitectura del conjunto de instrucciones (ISA) del dispositivo backend y deben tener en cuenta las puertas básicas y la conectividad de los qubits del dispositivo.

El siguiente código instancia un dispositivo real al que enviar un trabajo y transforma el circuito y los observables para que coincidan con la ISA de ese backend. Es necesario que ya haya guardado sus credenciales.

service = QiskitRuntimeService()

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

# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

isa_circuit.draw("mpl", idle_wires=False)

Output:

Output of the previous code cell

Paso 3. Ejecutar utilizando las primitivas cuánticas

Los ordenadores cuánticos pueden generar resultados aleatorios, por lo que normalmente se recopila una muestra de los resultados ejecutando el circuito varias veces. Puedes calcular el valor de la variable observable utilizando la Estimator clase. Estimator es una de las dos funciones primitivas; la otra es Sampler, que se puede utilizar para obtener datos de un ordenador cuántico. Estos objetos disponen de un run() método que lleva a cabo la selección de circuitos, observables y parámetros (si procede), utilizando un bloque unificado primitivo ( PUB ).

# Construct the Estimator instance.

estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000

mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

# One pub, with one circuit to run against five different observables.
job = estimator.run([(isa_circuit, mapped_observables)])

# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")

Output:

>>> Job ID: d9bjj3u6hjac73ffpth0

Después de enviar un trabajo, puede esperar hasta que se complete dentro de su instancia actual de Python o utilizar job_id para recuperar los datos más tarde. (Consulte la sección sobre recuperación de trabajos para obtener más detalles)

Una vez completado el trabajo, examine su resultado a través del atributo result() del trabajo.

# This is the result of the entire submission.  You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()

# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]
Alternativa: ejecutar el ejemplo utilizando un simulador

Cuando ejecutas tu programa cuántico en un dispositivo real, tu carga de trabajo debe esperar en una cola antes de ejecutarse. Para ahorrar tiempo, puede utilizar el siguiente código para ejecutar esta pequeña carga de trabajo en el fake_provider con el modo de Qiskit Runtime prueba local. Tenga en cuenta que esto solo es posible para un circuito pequeño. Cuando amplíes la siguiente sección, necesitarás utilizar un dispositivo real.


# Use the following code instead if you want to run on a simulator:

from qiskit_ibm_runtime.fake_provider import FakeBelemV2
backend = FakeBelemV2()
estimator = Estimator(backend)

# Convert to an ISA circuit and layout-mapped observables.

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

job = estimator.run([(isa_circuit, mapped_observables)])
result = job.result()

# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).

job_result = job.result()

# This is the result from our single pub, which had five observables,
# so contains information on all five.

pub_result = job.result()[0]

Paso 4. Analizar los resultados

El paso de análisis es normalmente donde se pueden procesar los resultados utilizando, por ejemplo, la mitigación del error de medición o la extrapolación de ruido cero (ZNE). Puede introducir estos resultados en otro flujo de trabajo para realizar análisis adicionales o preparar un gráfico con los valores y datos clave. En general, este paso es específico para su problema. Para este ejemplo, represente gráficamente cada uno de los valores esperados que se midieron para nuestro circuito.

Se puede acceder a los valores esperados y a las desviaciones estándar de las magnitudes observables que se han especificado en Estimator a través de los atributos y PubResult.data.stds del PubResult.data.evs resultado del trabajo. Para obtener los resultados de Sampler, utiliza la PubResult.data.meas.get_counts() función, que devolverá un dict conjunto de mediciones en forma de cadenas de bits como claves y recuentos como sus valores correspondientes. Para obtener más información, consulta la guía de inicio rápido de Sampler.

# Plot the result

values = pub_result.data.evs

errors = pub_result.data.stds

# plotting graph
plt.plot(observables_labels, values, "-o")
plt.xlabel("Observables")
plt.ylabel("Values")
plt.show()

Output:

Output of the previous code cell

Obsérvese que para los qubits 0 y 1, los valores esperados independientes tanto de X como de Z son 0, mientras que las correlaciones (XX y ZZ) son 1. Esta es una característica distintiva del entrelazamiento cuántico.


Escalar a un gran número de qubits

En la informática cuántica, el trabajo a escala industrial es crucial para avanzar en este campo. Este tipo de trabajo requiere realizar cálculos a una escala mucho mayor, trabajando con circuitos que pueden utilizar más de 100 qubits y más de 1000 puertas. Este ejemplo muestra cómo se puede realizar un trabajo a escala industrial en IBM® QPU creando y analizando un estado GHZ de 100 qubits. Utiliza el flujo de trabajo de patrones de Qiskit y termina midiendo el valor esperado Z0Zi\langle Z_0 Z_i \rangle para cada qubit.

Paso 1. Identificar el problema

Escribe una función que devuelva un QuantumCircuit que prepare un nn estado GHZ de qubits (esencialmente un estado Bell extendido) y, a continuación, utiliza esa función para preparar un estado GHZ de 100 qubits y recopilar los observables que se van a medir.

def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:
    """This function will create a qiskit.QuantumCircuit (qc)
        for an n-qubit GHZ state.

    Args:
        n (int): Number of qubits in the n-qubit GHZ state

    Returns:
        QuantumCircuit: Quantum circuit that generate the n-qubit GHZ state,
            assuming all qubits start in the 0 state
    """
    if isinstance(n, int) and n >= 2:
        qc = QuantumCircuit(n)
        qc.h(0)
        for i in range(n - 1):
            qc.cx(i, i + 1)
    else:
        raise Exception("n is not a valid input")
    return qc


# Create a new circuit with 100 qubits in the GHZ state
n = 100
qc = get_qc_for_n_qubit_GHZ_state(n)

A continuación, asigne los operadores de interés. Este ejemplo utiliza los ZZ operadores entre qubits para examinar el comportamiento a medida que se alejan entre sí. Los valores de expectativa cada vez más inexactos (corruptos) entre qubits distantes revelarían el nivel de ruido presente.

# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
    "Z" + "I" * i + "Z" + "I" * (n - 2 - i) for i in range(n - 1)
]

operators = [SparsePauliOp(operator) for operator in operator_strings]

Paso 2. Optimizar el problema para su ejecución en hardware cuántico

El siguiente código transforma el circuito y los observables para que coincidan con la ISA del backend. Es necesario que ya haya guardado sus credenciales.

service = QiskitRuntimeService()

backend = service.least_busy(
    simulator=False, operational=True, min_num_qubits=100
)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)

isa_circuit = pm.run(qc)
isa_operators_list = [op.apply_layout(isa_circuit.layout) for op in operators]

Paso 3. Ejecutar en hardware

Envía el trabajo y activa la supresión de errores utilizando una técnica para reducir los errores denominada «desacoplamiento dinámico ». El nivel de resiliencia indica el grado de resiliencia que se debe implementar frente a los errores. Los niveles más altos generan resultados más precisos, aunque a costa de tiempos de procesamiento más largos. Para obtener más información sobre las opciones configuradas en el siguiente código, consulte «Configurar la mitigación de errores para Qiskit Runtime ».

options = EstimatorOptions()
options.resilience_level = 1
options.dynamical_decoupling.enable = True
options.dynamical_decoupling.sequence_type = "XY4"

# Create an Estimator object
estimator = Estimator(backend, options=options)
# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
print(job_id)

Output:

d9bk5brv6alc73ct68ag

Paso 4. Resultados del procesamiento posterior

Una vez completado el trabajo, represente gráficamente los resultados y observe que Z0Zi\langle Z_0 Z_i \rangle disminuye al aumentar ii, aunque en una simulación ideal todos Z0Zi\langle Z_0 Z_i \rangle los deberían ser 1.

# data
data = list(range(1, len(operators) + 1))  # Distance between the Z operators
result = job.result()[0]
values = result.data.evs  # Expectation value at each Z operator.
values = [
    v / values[0] for v in values
]  # Normalize the expectation values to evaluate how they decay with distance.

# plotting graph
plt.plot(data, values, marker="o", label="100-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()

Output:

Output of the previous code cell

El gráfico anterior muestra que, a medida que aumenta la distancia entre los qubits, la señal se debilita debido a la presencia de ruido.


Próximos pasos

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