Skip to main content
IBM Quantum Platform

Simular un modelo de Ising con la función TEM

El método de mitigación de errores de red tensorial (TEM) de Algorithmiq es un algoritmo híbrido cuántico-clásico diseñado para realizar la mitigación del ruido íntegramente en la etapa de posprocesamiento clásico. Con TEM, el usuario puede calcular los valores esperados de los observables, mitigando los inevitables errores inducidos por el ruido que se producen en el hardware cuántico con mayor precisión y rentabilidad, lo que lo convierte en una opción muy atractiva tanto para los investigadores cuánticos como para los profesionales del sector.

Este tutorial muestra cómo TEM puede obtener resultados significativos para la dinámica de un sistema cuántico, lo cual sería inaccesible sin la mitigación de errores y requeriría muchos más recursos cuánticos si se utilizaran otros métodos de mitigación de errores, como PEC y ZNE.

Estimación de uso: este cuaderno utiliza aproximadamente 10 minutos de QPU en dispositivos Heron r3. El tiempo de ejecución puede depender en gran medida del dispositivo elegido. A continuación se pueden encontrar estimaciones de uso por sección.


Realice experimentos de física de muchos cuerpos con mitigación de errores con la función TEM

Este tutorial se basa en la siguiente referencia: L. E. Fischer et al., Nat. Fís. (2026). Esta referencia analiza una simulación real en hardware cuántico de hasta 91 qubits. En este tutorial, recreamos una simulación similar en un circuito de menor tamaño.

El modelo de Ising modificado corresponde al modelo de Ising habitual:

H^I=Jn=0N2Z^nZ^n+1+hn=0N1Z^n\hat{H}_{\text{I}} = J \sum_{n=0}^{N-2} \hat{Z}_n \hat{Z}_{n+1} + h \sum_{n=0}^{N-1} \hat{Z}_n

al que se le aplica una patada transversal:

H^K=bn=0N1X^n\hat{H}_{K} = b \sum_{n=0}^{N-1} \hat{X}_n

El objetivo es simular la dinámica de un estado bajo el hamiltoniano de Ising transversal, cuya evolución temporal puede implementarse mediante un operador unitario de Floquet U^KI=eiH^KeiH^I\hat{U}_{\text{KI}} = e^{-i \hat{H}_K} e^{-i \hat{H}_I} . El estado inicial que se va a desarrollar es aquel en el que el primer qubit se encuentra en el estado +|+\rangle, mientras que los demás se emparejan y se establecen en el estado de Bell (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}.

La cantidad que queremos observar es la función de correlación. El artículo de referencia analiza cómo esta cantidad puede reescribirse como un operador de Pauli e X^\hat{X} e en el qubit e nthn^{th}. Tras una serie de pasos de tiempo físicos tt, calculamos el valor del operador de Pauli X^n=t\hat{X}_{n=t}. Dependiendo de los parámetros del sistema, el valor de esta observable es igual a un valor que puede calcularse con exactitud o solo simularse mediante métodos aproximados. Concretamente, para J=b=π/4|J|=|b|=\pi/4 es igual a [cos(2h)]t[\cos(2h)]^t, que es el valor que utilizaremos para comparar los resultados de este tutorial. Además, en un intervalo de tiempo dado tt, X^nt\langle\hat{X}_{n\neq t}\rangle es cero. Para obtener más detalles sobre cómo obtener estos valores y para compararlos con los resultados aproximados de la simulación clásica fuera de estos parámetros, véase L. E. Fischer et al., Nat. Fís. (2026).

TEM funciona caracterizando primero el ruido para cada capa única de puertas de dos qubits en el circuito, así como caracterizando el error de lectura. A continuación, el circuito se ejecuta en la máquina cuántica. Por último, la mitigación del error de la red tensorial se realiza en los recursos clásicos de IBM Cloud® y se devuelve el valor mitigado. En este ejemplo, el circuito tiene dos capas únicas que caracterizar.


Configuración

Como requisito previo, asegúrese de que las dependencias necesarias estén instaladas.

%pip install numpy matplotlib qiskit qiskit-ibm-catalog qiskit-ibm-runtime pylatexenc qiskit_qasm3_import
import os
from matplotlib import pyplot as plt
import numpy as np

from qiskit.quantum_info import SparsePauliOp
from qiskit.qasm3 import load

from qiskit_ibm_catalog import QiskitFunctionsCatalog

Mitigación de errores con TEM

Aquí proporcionamos un circuito que implementa el modelo de Ising con efecto de retroceso descrito anteriormente. El circuito se prepara de la siguiente manera. En primer lugar, hay una fase de preparación del estado, en la que el primer qubit se encuentra en el estado +|+\rangle, mientras que los demás se encuentran en pares de Bell (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}. A continuación, se aplica la estructura de ladrillo que implementa la evolución unitaria U^KI\hat{U}_{\text{KI}}. El número de pasos de tiempo físicos corresponde a t/2t/2 capas del circuito.

El siguiente código descarga los dos archivos QASM necesarios para este tutorial.

# Download required QASM files
import urllib

urllib.request.urlretrieve(
    "https://ibm.box.com/shared/static/swy5jtq309b0xpzluzlmsmj908yphes8.qasm",
    "ki_30q.qasm",
)
urllib.request.urlretrieve(
    "https://ibm.box.com/shared/static/et3gkodonw6gsp2trs43lzaozrdtiu7s.qasm",
    "ki_12q.qasm",
)

Podemos visualizar una versión reducida del circuito, con 12 qubits y seis pasos temporales:

# Parameters of the kicked Ising model
h = 0.0
num_qubits = 12
t_steps = 6

# Load the circuit for the kicked Ising model
small_circuit = load("ki_12q.qasm")

# Draw the circuit
small_circuit.draw("mpl", scale=0.25, fold=-1)

Output:

Output of the previous code cell

A continuación, construya el observable, X^n=t\hat{X}_{n=t}. Se construye como una cadena de Pauli simple con el orden que coincide con el utilizado por Qiskit:

def xt_observable(n_qubits, t_steps):
    pauli_str = "".join(["I" * t_steps, "X", "I" * (n_qubits - t_steps - 1)])
    pauli_str = pauli_str[::-1]  # Reverse the string to match qiskit order
    return SparsePauliOp(data=pauli_str, coeffs=1.0)

En nuestro pequeño ejemplo de 12 qubits, el observable tiene este aspecto:

# Build the observable for the kicked Ising model
small_observable = xt_observable(n_qubits=12, t_steps=6)
print(small_observable)

Output:

SparsePauliOp(['IIIIIXIIIIII'],
              coeffs=[1.+0.j])

Qiskit Functions utilizar los PUB como medio para recopilar las aportaciones. En nuestro caso, consideremos un único circuito y un observador como nuestro PUB :

# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(small_circuit, [small_observable])]

A continuación, accedemos a la función TEM. Primero configuramos la autenticación necesaria en IBM Cloud y seleccionamos un backend de entre los dispositivos disponibles. El token, los backends disponibles y los nombres de recursos en la nube (CRN) correspondientes se pueden obtener iniciando sesión en su cuenta en el panel de control de IBM Quantum Platform.

# Set IBM Quantum credentials and backend configuration
personal_token = os.environ.get(
    "QISKIT_IBM_TOKEN", "<API-KEY>"
)  # Replace with your personal token or set the environment variable
channel = "ibm_quantum_platform"
crn = "your_crn"  # Replace with the Cloud Resource Name (CRN)

# Select the QPU backend
backend_name = "ibm_qpu_name"  # Replace with your desired backend's name

Cargue la función TEM desde Qiskit Functions Catalog :

# Load the TEM function from the Qiskit Functions Catalog
catalog = QiskitFunctionsCatalog(
    channel=channel,
    token=personal_token,
    instance=crn,
)
tem = catalog.load("algorithmiq/tem")

Ahora podemos realizar un experimento en el circuito Ising activado con la mitigación de errores proporcionada por TEM. Con la configuración predeterminada, TEM se puede ejecutar de forma sencilla con un tiempo de ejecución de QPU previsto de entre 2.5 minutos, dependiendo de la QPU:

tem_job = tem.run(pubs=pubs, backend_name=backend_name)

Con las opciones predeterminadas, la función TEM ejecuta tres tareas en el ordenador cuántico: aprendizaje del ruido, mitigación de la lectura y muestreo del circuito. El número de disparos utilizados por cada uno de ellos se puede cambiar en las opciones pasadas a la función. Por defecto, estos parámetros se establecen para lograr una precisión de 0.05 en los valores esperados mitigados.

Puede comprobar el estado de su trabajo en el panel de control de IBM Quantum Platform o con:

print(tem_job.status())

Output:

QUEUED

Cuando el estado es DONE, podemos comprobar los resultados sin procesar y mitigados. Los tem_evs definidos a continuación son los valores esperados de los observables solicitados, en este caso solo un observable, X^n=t\langle \hat X_{n=t}\rangle, y tem_std son las desviaciones estándar correspondientes.

# Get the results of the TEM job
tem_results = tem_job.result()[
    0
]  # Get the first and only result from the job
tem_evs = tem_results.data.evs[0]
tem_std = tem_results.data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")

Output:

TEM Result: 1.031 ± 0.046

También podemos comprobar cuánto tiempo de ejecución cuántico se utilizó para cada llamada en IBM Quantum Platform, o inspeccionando los metadatos del resultado del código Python.

# Get the TEM job runtime
tem_runtime = tem_job.result().metadata["resource_usage"][
    "RUNNING: EXECUTING_QPU"
]["QPU_TIME"]

print(f"TEM Runtime: {tem_runtime} seconds")

Output:

TEM Runtime: 155.0 seconds

Personalizar los parámetros TEM y las opciones avanzadas

La función TEM ofrece varias opciones avanzadas para personalizar el flujo de trabajo de mitigación de errores. Estas opciones le permiten controlar la precisión, el número de disparos, las estrategias de aprendizaje del ruido y otros parámetros para adaptarse mejor a los requisitos de su experimento y a los recursos cuánticos disponibles.

Las opciones avanzadas comunes son:

  • precision: Especifique la precisión objetivo para los valores esperados mitigados.
  • default_shots: En lugar de precision, puede especificar el número de disparos utilizados por el trabajo de medición.
  • tem_max_bond_dimension: La dimensión máxima del enlace utilizada en la red tensorial.
  • tem_compression_cutoff: El valor de corte que se utilizará para la red tensorial.
  • Opciones de aprendizaje del ruido : Configure cómo se caracteriza el ruido, como el número de repeticiones o circuitos de calibración específicos.
  • **private**Asegúrese de que los circuitos y los resultados de los experimentos sean privados y desactive las descargas múltiples de los resultados de los trabajos.

Consulte la documentación de TEM o el sitio web Qiskit Functions Catalog para obtener una lista completa de las opciones compatibles y sus descripciones. Puede ajustar estos parámetros para equilibrar el tiempo de ejecución, el uso de recursos y la precisión de los resultados.

Puede pasar estas opciones como un diccionario al options argumento al ejecutar la función TEM:

options = {
    "default_shots": 10_000,
    "tem_max_bond_dimension": 512,
    "tem_compression_cutoff": 1e-16,
    # This option helps optimizing the measurement
    # stage since the observable is strongly biased
    # toward the X operator for all the qubits.
    "compute_shadows_bias_from_observable": True,
    # set to True to keep experiment results private,
    # recommended for confidential circuits
    "private": False,
}

También se pueden pasar opciones personalizadas para el aprendedor de ruido. Siguen las definiciones utilizadas en el Diccionario de terminología de la UE ( Qiskit Runtime ) NoiseLearnerOptions:

nl_options = {
    "num_randomizations": 32,
    "max_layers_to_learn": 2,
    "shots_per_randomization": 128,
    "layer_pair_depths": [0, 1, 2, 4, 16, 32],
}

# add noise learning options to the overall options
options |= nl_options

Vuelva a realizar el experimento con estas opciones personalizadas ajustadas a nuestro circuito. El tiempo de ejecución previsto es de aproximadamente cuatro minutos QPU.

tem_job_custom = tem.run(
    pubs=pubs, backend_name=backend_name, options=options
)

Si el trabajo no está configurado como privado, podemos recuperar el resultado más adelante. Para ello, guarde el ID del trabajo que aparece aquí y utilice tem_job_custom = catalog.get_job_by_id("your-job-id").

job_id = tem_job_custom.job_id
print(f"Job ID: {job_id}")

Output:

Job ID: 1ba10094-a541-457a-9287-dbd49306d12d
results_custom = tem_job_custom.result()
tem_evs = results_custom[0].data.evs[0]
tem_std = results_custom[0].data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")

Output:

TEM Result: 0.956 ± 0.018

Ahora podemos inspeccionar los resultados y los metadatos para obtener información sobre el experimento:

metadata_custom = results_custom[0].metadata

unmitigated_evs = metadata_custom["evs_non_mitigated"][0]
unmitigated_stds = metadata_custom["stds_non_mitigated"][0]
print(f"Unmitigated Result: {unmitigated_evs:.3f} ± {unmitigated_stds:.3f}")

# Exact result for the kicked Ising model from the reference paper
exact_evs = np.cos(2 * h) ** t_steps
print("Exact Result:", exact_evs)

Output:

Unmitigated Result: 0.894 ± 0.015
Exact Result: 1.0
# Plot comparing the different expectation values
plt.bar(
    ["Unmitigated", "TEM"],
    [unmitigated_evs, tem_evs],
    yerr=[unmitigated_stds, tem_std],
    color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()

Output:

Output of the previous code cell

Por último, podemos comprobar el impacto de las opciones personalizadas en el tiempo de ejecución de la QPU y el tiempo de ejecución clásico:

# Get the metadata of the TEM job
job_metadata = results_custom.metadata

# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
    "QPU_TIME"
]
classical_runtime = (
    job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
        "CPU_TIME"
    ]
    + job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)

print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")

Output:

QPU Runtime: 342.0 seconds
Classical Runtime: 107.632604 seconds

Escalar TEM a circuitos grandes

En principio, los circuitos grandes pueden ejecutarse con la función TEM. Sin embargo, es importante ser consciente de las limitaciones de los recursos clásicos, ya que TEM se ejecuta en ejecutores de IBM Cloud, con tiempos de ejecución potencialmente muy largos. Para circuitos extremadamente grandes, póngase en contacto con el equipo de asistencia de TEM en qiskit\ [email protected].

Aquí ejecutamos un ejemplo con un circuito más grande, del tamaño de una planta de servicios públicos, de 30 qubits, optimizando los parámetros TEM para la velocidad en lugar de la precisión.

# Kicked Ising model parameters
n_qubits = 30
t_steps = 15
h = 0.0

# Load the circuit for the kicked Ising model
circuit = load("ki_30q.qasm")


# Build the observable for the kicked Ising model
observable = xt_observable(n_qubits=n_qubits, t_steps=t_steps)

# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(circuit, [observable])]

Definamos algunas opciones orientadas al rendimiento:

options = {
    "num_randomizations": 32,
    "max_layers_to_learn": 2,
    "shots_per_randomization": 128,
    "layer_pair_depths": [0, 1, 2, 4, 16, 32, 64],
    "default_shots": 5_000,
    "tem_max_bond_dimension": 128,
    "tem_compression_cutoff": 1e-10,
    "compute_shadows_bias_from_observable": True,
    "private": False,
}

Por último, realice el experimento, obtenga el resultado y visualícelo. Esto llevará aproximadamente 3.5 minutos de QPU.

tem_job_large = tem.run(pubs=pubs, backend_name=backend_name, options=options)
job_id = tem_job_large.job_id
print(f"Job ID: {job_id}")

Output:

Job ID: 9f3f190f-f4b0-4dcb-bb83-5f71f37d0d77
results_large = tem_job_large.result()
tem_evs = results_large[0].data.evs[0]
tem_std = results_large[0].data.stds[0]

print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")


# Get the metadata of the TEM job
job_metadata = tem_job_large.result().metadata

# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
    "QPU_TIME"
]
classical_runtime = (
    job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
        "CPU_TIME"
    ]
    + job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)

print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")

Output:

TEM Result: 0.794 ± 0.026
QPU Runtime: 203.0 seconds
Classical Runtime: 251.71805499999996 seconds
# Plot comparing the different expectation values
metadata_large = results_large[0].metadata
unmitigated_evs = metadata_large["evs_non_mitigated"][0]
unmitigated_stds = metadata_large["stds_non_mitigated"][0]

exact_evs = np.cos(2 * h) ** t_steps

plt.bar(
    ["Unmitigated", "TEM"],
    [unmitigated_evs, tem_evs],
    yerr=[unmitigated_stds, tem_std],
    color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()

Output:

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