Přeskočit na hlavní obsah

Simulace modelu kicked Ising s funkcí TEM

Metoda Tensor-network Error Mitigation (TEM) od Algorithmiq je hybridní kvantově-klasický algoritmus navržený pro provádění zmírňování šumu výhradně v rámci klasické fáze post-processingu. Pomocí TEM může uživatel vypočítat střední hodnoty obzervovatelných, čímž zmírní nevyhnutelné chyby způsobené šumem na kvantovém hardwaru s vyšší přesností a nákladovou efektivitou, díky čemuž je velmi atraktivní volbou jak pro kvantové výzkumníky, tak pro průmyslové praktiky.

Tento tutoriál ukazuje, jak TEM může získat smysluplné výsledky pro dynamiku kvantového systému, které by byly nedostupné bez zmírňování chyb a které vyžadují podstatně více kvantových zdrojů při použití jiných metod zmírňování chyb, jako jsou PEC a ZNE.

Odhad využití: Tento notebook využívá přibližně 10 minut QPU na zařízeních Heron r3. Doba běhu může výrazně záviset na zvoleném zařízení. Odhady využití pro jednotlivé části lze nalézt níže.

Spuštění experimentů s fyzikou mnoha těles s zmírňováním chyb pomocí funkce TEM

Tento tutoriál je založen na následující referenci: L. E. Fischer et al., Nat. Phys. (2026). Tato reference diskutuje skutečnou simulaci na kvantovém hardwaru až 91 qubitů. V tomto tutoriálu znovu vytvoříme podobnou simulaci na menší velikosti obvodu.

Model kicked Ising odpovídá obvyklému modelu Ising:

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

na který je aplikován příčný kick:

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

Cílem je simulovat dynamiku stavu pod hamiltonianem příčného kicked Isinga, jehož časový vývoj lze implementovat Floquetovým unitárním operátorem U^KI=eiH^KeiH^I\hat{U}_{\text{KI}} = e^{-i \hat{H}_K} e^{-i \hat{H}_I} . Počátečním stavem pro vývoj je ten, ve kterém je první qubit ve stavu +|+\rangle, zatímco ostatní jsou spárovány a nastaveny do Bellova stavu (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}.

Veličinou, kterou chceme pozorovat, je korelační funkce. Referenční práce popisuje, jak lze tuto veličinu přepsat jako operátor Pauliho X^\hat{X} na nn-tém qubitu. Po určitém počtu fyzikálních časových kroků tt vypočítáme hodnotu operátoru Pauliho X^n=t\hat{X}_{n=t}. V závislosti na parametrech systému se hodnota této obzervovatelné rovná hodnotě, kterou lze vypočítat přesně, nebo jen přibližnými metodami. Konkrétně pro J=b=π/4|J|=|b|=\pi/4 se rovná [cos(2h)]t[\cos(2h)]^t, což je hodnota, kterou použijeme jako referenci pro výsledky tohoto tutoriálu. Navíc pro daný časový krok tt je X^nt\langle\hat{X}_{n\neq t}\rangle rovno nule. Podrobnosti k získání těchto hodnot a porovnání s přibližnými výsledky klasické simulace mimo tyto parametry viz L. E. Fischer et al., Nat. Phys. (2026).

TEM funguje tak, že nejprve charakterizuje šum pro každou jedinečnou vrstvu dvouqubitových hradel v obvodu, a také charakterizuje chybu čtení. Poté je obvod proveden na kvantovém stroji. Nakonec je zmírňování chyb tenzorové sítě provedeno na klasických zdrojích IBM Cloud® a je vrácena zmírněná hodnota. V tomto příkladu má obvod dvě jedinečné vrstvy ke charakterizaci.

Nastavení

Jako předpoklad se ujisti, že jsou nainstalované potřebné závislosti.

%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

Zmírňování chyb s TEM

Zde poskytujeme obvod implementující model kicked Ising popsaný výše. Obvod je připraven takto. Nejprve proběhne fáze přípravy stavu, ve které je první qubit ve stavu +|+\rangle, zatímco ostatní jsou v Bellových párech (00+11)/2(|00\rangle + |11\rangle)/\sqrt{2}. Po ní následuje cihlová struktura implementující unitární vývoj U^KI\hat{U}_{\text{KI}}. Počet fyzikálních časových kroků odpovídá t/2t/2 vrstvám obvodu. Následující kód stáhne dva soubory QASM potřebné pro tento tutoriál.

# 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",
)

Můžeme vizualizovat malou verzi obvodu s 12 qubity a šesti časovými kroky:

# 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 of the previous code cell

Dále vytvoř obzervovatelnou X^n=t\hat{X}_{n=t}. Je konstruována jako jednoduchý Pauliho řetězec s pořadím odpovídajícím tomu, které používá 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)

V našem malém 12-qubitovém příkladu obzervovatelná vypadá takto:

# Build the observable for the kicked Ising model
small_observable = xt_observable(n_qubits=12, t_steps=6)
print(small_observable)
SparsePauliOp(['IIIIIXIIIIII'],
coeffs=[1.+0.j])

Funkce Qiskit používají PUBy jako způsob shromažďování vstupů. V našem případě uvažujme jediný obvod a obzervovatelnou jako náš PUB:

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

Dále získáme přístup k funkci TEM. Nejprve nastavíme potřebné ověřování pro IBM Cloud a vybereme backend z dostupných zařízení. Token, dostupné backendy a odpovídající názvy cloudových zdrojů (CRN) lze získat přihlášením se ke svému účtu na řídicím panelu 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

Načti funkci TEM z Katalogu funkcí Qiskit:

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

Nyní můžeme spustit experiment na obvodu kicked Ising se zmírňováním chyb poskytovaným TEM. Při použití výchozích nastavení lze TEM spustit jednoduchým způsobem s očekávanou dobou běhu QPU přibližně 2,5 minuty v závislosti na QPU:

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

S výchozími možnostmi spustí funkce TEM na kvantovém počítači tři úlohy: učení šumu, zmírňování čtení a vzorkování obvodu. Počet shotů použitých každou z nich lze změnit v možnostech předaných funkci. Ve výchozím nastavení jsou tyto parametry nastaveny tak, aby dosáhly přesnosti 0,05 ve zmírněných střední hodnotách. Stav své úlohy můžeš zkontrolovat na řídicím panelu IBM Quantum Platform nebo pomocí:

print(tem_job.status())
QUEUED

Pokud je stav DONE, můžeme zkontrolovat surové a zmírněné výsledky. tem_evs definované níže jsou střední hodnoty požadovaných obzervovatelných, v tomto případě pouze jedné obzervovatelné, X^n=t\langle \hat X_{n=t}\rangle, a tem_std jsou odpovídající směrodatné odchylky.

# 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}")
TEM Result: 1.031 ± 0.046

Můžeme také zkontrolovat, kolik kvantového runtime bylo použito pro každé volání na IBM Quantum Platform, nebo kontrolou metadat výsledků z kódu 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")
TEM Runtime: 155.0 seconds

Přizpůsobení parametrů TEM a pokročilých možností

Funkce TEM poskytuje několik pokročilých možností pro přizpůsobení pracovního postupu zmírňování chyb. Tyto možnosti ti umožňují řídit přesnost, počet shotů, strategie učení šumu a další parametry, aby lépe vyhovovaly požadavkům tvého experimentu a dostupným kvantovým zdrojům.

Běžné pokročilé možnosti jsou:

  • precision: Zadej cílovou přesnost pro zmírněné střední hodnoty.
  • default_shots: Místo precision můžeš zadat počet shotů používaných měřicí úlohou.
  • tem_max_bond_dimension: Maximální dimenze vazby používaná v tenzorové síti.
  • tem_compression_cutoff: Mezní hodnota pro tenzorovou síť.
  • Možnosti učení šumu: Konfiguruj způsob charakterizace šumu, například počet opakování nebo specifické kalibrační obvody.
  • private: Zajisti soukromí obvodů a výsledků experimentů a zakáž vícenásobné stahování výsledků úloh.

Úplný seznam podporovaných možností a jejich popis najdeš v dokumentaci TEM nebo v Katalogu funkcí Qiskit. Tyto parametry můžeš upravit tak, aby se vyvážila doba běhu, využití zdrojů a přesnost výsledků. Tyto možnosti můžeš předat jako slovník argumentu options při spuštění funkce 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,
}

Lze také předat vlastní možnosti pro učení šumu. Řídí se definicemi používanými v 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

Znovu spusť experiment s těmito vlastními možnostmi přizpůsobenými pro náš obvod. Očekávaná doba běhu je přibližně čtyři minuty QPU.

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

Pokud úloha není nastavena jako soukromá, můžeme výsledek získat zpět později. Chceš-li to provést, ulož zde vytištěné ID úlohy a použij tem_job_custom = catalog.get_job_by_id("your-job-id").

job_id = tem_job_custom.job_id
print(f"Job ID: {job_id}")
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}")
TEM Result: 0.956 ± 0.018

Nyní můžeme prozkoumat výsledky a metadata a získat přehled o experimentu:

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)
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 of the previous code cell

Nakonec můžeme zkontrolovat dopad vlastních možností na dobu běhu QPU a klasického procesoru:

# 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")
QPU Runtime: 342.0 seconds
Classical Runtime: 107.632604 seconds

Škálování TEM na velké obvody

Velké obvody lze v zásadě spustit s funkcí TEM. Je však důležité být si vědom omezení klasických zdrojů, protože TEM je spouštěn na serverech IBM Cloud s potenciálně velmi dlouhou dobou běhu. Pro extrémně velké obvody kontaktuj tým podpory TEM na qiskit_ibm@algorithmiq.fi.

Zde spustíme příklad s větším obvodem 30 qubitů ve škále utility, přičemž parametry TEM optimalizujeme pro rychlost, nikoli přesnost.

# 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])]

Definujme několik možností zaměřených na výkon:

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,
}

Nakonec spusť experiment, získej výsledek a vizualizuj ho. Bude to trvat přibližně 3,5 minuty 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}")
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")
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 of the previous code cell