Simula un modello di Ising con kick utilizzando la funzione TEM
Il metodo TEM (Tensor-network Error Mitigation) di Algorithmiq è un algoritmo ibrido quantistico-classico progettato per eseguire la mitigazione del rumore interamente nella fase di post-elaborazione classica. Con il TEM, l'utente può calcolare i valori attesi degli osservabili, mitigando gli inevitabili errori indotti dal rumore che si verificano sull'hardware quantistico con maggiore precisione ed efficienza in termini di costi, rendendolo un'opzione molto interessante sia per i ricercatori quantistici che per i professionisti del settore.
Questo tutorial mostra come TEM possa ottenere risultati significativi per le dinamiche di un sistema quantistico, che sarebbero inaccessibili senza la mitigazione degli errori e che richiederebbero risorse quantistiche notevolmente maggiori se si utilizzassero altri metodi di mitigazione degli errori come PEC e ZNE.
Stima di utilizzo: questo notebook utilizza circa 10 minuti QPU sui dispositivi Heron r3. Il tempo di esecuzione può dipendere in modo sostanziale dal dispositivo scelto. Di seguito sono riportate le stime di utilizzo per sezione.
Esegui esperimenti di fisica dei sistemi a molti corpi con mitigazione degli errori utilizzando la funzione TEM
Questo tutorial si basa sul seguente riferimento: L. E. Fischer et al., Nat. Fisico. (2026). Questo riferimento tratta una simulazione reale su hardware quantistico fino a 91 qubit. In questo tutorial ricreiamo una simulazione simile su un circuito di dimensioni inferiori.
Il modello di Ising modificato corrisponde al modello di Ising tradizionale:
a cui viene applicato un calcio trasversale:
L'obiettivo è simulare le dinamiche di uno stato sotto l'Hamiltoniano di Ising con kick trasversale, la cui evoluzione temporale può essere implementata da un' unitaria di Floquet. Lo stato iniziale da evolvere è quello in cui il primo qubit si trova nello stato , mentre gli altri sono accoppiati e impostati nello stato di Bell .
La quantità che vogliamo osservare è la funzione di correlazione. Il documento di riferimento discute come questa quantità possa essere riscritta come un operatore di Pauli di tipo " " sul qubit " ". Dopo una serie di passi temporali fisici , calcoliamo il valore dell'operatore di Pauli . A seconda dei parametri del sistema, il valore di questa osservabile è uguale a un valore che può essere calcolato con esattezza o solo simulato con metodi approssimativi. Nello specifico, per è uguale a , che è il valore che useremo per confrontare i risultati di questo tutorial. Inoltre, in un dato intervallo di tempo , è pari a zero. Per i dettagli su come ottenere questi valori e per un confronto con i risultati approssimativi della simulazione classica al di fuori di questi parametri, si veda L. E. Fischer et al., Nat. Fisico. (2026).
Il TEM funziona caratterizzando innanzitutto il rumore per ogni singolo strato di porte a due qubit nel circuito, oltre a caratterizzare l'errore di lettura. Quindi, il circuito viene eseguito sulla macchina quantistica. Infine, la mitigazione dell'errore della rete tensoriale viene eseguita sulle risorse classiche in IBM Cloud® e viene restituito il valore mitigato. In questo esempio, il circuito presenta due livelli distinti da caratterizzare.
Configura
Come prerequisito, assicurarsi che siano installate le dipendenze necessarie.
%pip install numpy matplotlib qiskit qiskit-ibm-catalog qiskit-ibm-runtime pylatexenc qiskit_qasm3_importimport 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 QiskitFunctionsCatalogMitigazione degli errori con TEM
Qui forniamo un circuito che implementa il modello di Ising con kick descritto sopra. Il circuito è preparato come segue. In primo luogo, vi è una fase di preparazione dello stato, in cui il primo qubit si trova nello stato , mentre gli altri sono in coppie di Bell . Segue la struttura a mattoni che implementa l'evoluzione unitaria . Il numero di passi temporali fisici corrisponde agli strati del circuito .
Il codice seguente scarica i due file QASM necessari per questo 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",
)Possiamo visualizzare una versione ridotta del circuito, con 12 qubit e sei intervalli di tempo:
# 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:
Successivamente, crea l'osservabile, . È costruito come una semplice stringa di Pauli con l'ordine corrispondente a quello utilizzato da 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)Nel nostro piccolo esempio a 12 qubit, l'osservabile appare così:
# 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 utilizzare i PUB come mezzo per raccogliere i contributi. Nel nostro caso, consideriamo un singolo circuito e un osservabile come nostro PUB :
# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(small_circuit, [small_observable])]Successivamente, abbiamo accesso alla funzione TEM. Per prima cosa impostiamo l'autenticazione richiesta su IBM Cloud e selezioniamo un backend dai dispositivi disponibili. Il token, i backend disponibili e i nomi delle risorse cloud corrispondenti (CRN) possono essere ottenuti accedendo al proprio account sulla dashboard 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 nameCaricare la funzione TEM dall' Qiskit Functions Catalog (Libreria di funzioni di calcolo):
# Load the TEM function from the Qiskit Functions Catalog
catalog = QiskitFunctionsCatalog(
channel=channel,
token=personal_token,
instance=crn,
)
tem = catalog.load("algorithmiq/tem")Ora possiamo eseguire un esperimento sul circuito Ising avviato con la mitigazione degli errori fornita dal TEM. Utilizzando le impostazioni predefinite, TEM può essere eseguito in modo semplice con un tempo di esecuzione QPU previsto di circa 2.5 minuti, a seconda della QPU:
tem_job = tem.run(pubs=pubs, backend_name=backend_name)Con le opzioni predefinite, la funzione TEM esegue tre operazioni sul computer quantistico: apprendimento del rumore, mitigazione della lettura e campionamento dei circuiti. Il numero di scatti utilizzati da ciascuno di questi può essere modificato nelle opzioni passate alla funzione. Per impostazione predefinita, questi parametri sono configurati per ottenere una precisione dell' 0.05 e nei valori attesi mitigati.
Puoi controllare lo stato del tuo lavoro sulla dashboard di IBM Quantum Platform o con:
print(tem_job.status())Output:
QUEUED
Quando lo stato è DONE, possiamo controllare i risultati grezzi e mitigati. I valori tem_evs definiti di seguito sono i valori attesi degli osservabili richiesti, in questo caso un solo osservabile, , e tem_std sono le deviazioni standard corrispondenti.
# 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
È inoltre possibile verificare quanto tempo di esecuzione quantistico è stato utilizzato per ciascuna chiamata all'indirizzo IBM Quantum Platform o ispezionando i metadati dei risultati dal codice 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
Personalizza i parametri TEM e le opzioni avanzate
La funzione TEM offre diverse opzioni avanzate per personalizzare il flusso di lavoro di mitigazione degli errori. Queste opzioni consentono di controllare la precisione, il numero di scatti, le strategie di apprendimento del rumore e altri parametri per adattarsi meglio ai requisiti dell'esperimento e alle risorse quantistiche disponibili.
Le opzioni avanzate più comuni sono:
precision: Specificare la precisione desiderata per i valori attesi mitigati.default_shots: Invece diprecision, è possibile specificare il numero di scatti utilizzati dal lavoro di misurazione.tem_max_bond_dimension: La dimensione massima del legame utilizzata nella rete tensoriale.tem_compression_cutoff: Il valore di cutoff da utilizzare per la rete tensoriale.- Opzioni di apprendimento del rumore : configurare le caratteristiche del rumore, come il numero di ripetizioni o circuiti di calibrazione specifici.
private: Assicurati che i circuiti e i risultati degli esperimenti rimangano privati e disabilita il download multiplo dei risultati dei lavori.
Per un elenco completo delle opzioni supportate e delle relative descrizioni, consultare la documentazione TEM o il sito Qiskit Functions Catalog. È possibile regolare questi parametri per bilanciare il tempo di esecuzione, l'utilizzo delle risorse e l'accuratezza dei risultati.
È possibile passare queste opzioni come dizionario options all'argomento quando si esegue la funzione 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,
}È inoltre possibile specificare opzioni personalizzate per il modello di apprendimento del rumore. Seguono le definizioni utilizzate nel : qiskit-ibm-runtimeNoiseLearnerOptions
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_optionsRipeti l'esperimento con queste opzioni personalizzate ottimizzate per il nostro circuito. Il tempo di esecuzione previsto è di circa quattro minuti QPU.
tem_job_custom = tem.run(
pubs=pubs, backend_name=backend_name, options=options
)Se il lavoro non è impostato come privato, possiamo recuperare il risultato in un secondo momento. Per farlo, salva l'ID del lavoro stampato qui e usa 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
Ora possiamo esaminare i risultati e i metadati per ottenere informazioni dettagliate sull'esperimento:
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:
Infine, possiamo verificare l'impatto delle opzioni personalizzate sulla QPU e sul runtime classico:
# 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
Scala TEM per circuiti di grandi dimensioni
I circuiti di grandi dimensioni possono, in linea di principio, essere gestiti con la funzione TEM. Tuttavia, è importante essere consapevoli dei limiti delle risorse classiche, poiché il TEM viene eseguito su runner dell' IBM Cloud con tempi di esecuzione potenzialmente molto lunghi. Per circuiti di dimensioni estremamente grandi, contattare il team di assistenza TEM all'indirizzo qiskit\ [email protected].
Qui eseguiamo un esempio con un circuito più grande, di dimensioni pari a quelle utilizzate su scala industriale, da 30 qubit, ottimizzando i parametri TEM in termini di velocità piuttosto che di precisione.
# 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])]Definiamo alcune opzioni orientate alle prestazioni:
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,
}Infine, esegui l'esperimento, ottieni il risultato e visualizzalo. Ciò richiederà circa 3.5 minuti 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: