Skip to main content
IBM Quantum Platform

Simulieren Sie ein gekickt Ising-Modell mit der TEM-Funktion

Die Tensor-Network Error Mitigation (TEM)-Methode von Algorithmiq ist ein hybrider quantenklassischer Algorithmus, der für die vollständige Rauschunterdrückung innerhalb der klassischen Nachbearbeitungsphase entwickelt wurde. Mit TEM kann der Benutzer die Erwartungswerte von Observablen berechnen und so die unvermeidlichen rauschbedingten Fehler, die bei Quantenhardware auftreten, mit erhöhter Genauigkeit und Kosteneffizienz mindern, was es zu einer äußerst attraktiven Option für Quantenforscher und Industriepraktiker gleichermaßen macht.

Dieses Tutorial zeigt, wie TEM aussagekräftige Ergebnisse für die Dynamik eines Quantensystems erzielen kann, die ohne Fehlerminderung nicht zugänglich wären und die bei Verwendung anderer Fehlerminderungsmethoden wie PEC und ZNE wesentlich mehr Quantenressourcen erfordern würden.

Geschätzte Nutzung: Dieses Notebook verbraucht etwa 10 QPU-Minuten auf Heron- r3 -Geräten. Die Laufzeit kann erheblich vom gewählten Gerät abhängen. Die geschätzten Verbrauchswerte pro Abschnitt finden Sie unten.


Führen Sie mit der TEM-Funktion fehlerreduzierte Vielteilchenphysik-Experimente durch

Dieses Tutorial basiert auf folgender Referenz: L. E. Fischer et al., Nat. Phys. (2026). Diese Referenz behandelt eine reale Simulation auf Quantenhardware mit bis zu 91 Qubits. In diesem Tutorial erstellen wir eine ähnliche Simulation auf einer kleineren Schaltungsgröße.

Das gekickte Ising-Modell entspricht dem üblichen Ising-Modell:

H^I=J∑n=0N−2Z^nZ^n+1+h∑n=0N−1Z^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

auf den ein Querkick angewendet wird:

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

Das Ziel besteht darin, die Dynamik eines Zustands unter dem transversalen gekickten Ising-Hamiltonian zu simulieren, dessen zeitliche Entwicklung durch eine Floquet-Unitar U^KI=e−iH^Ke−iH^I\hat{U}_{\text{KI}} = e^{-i \hat{H}_K} e^{-i \hat{H}_I} implementiert werden kann. Der auszuführende Anfangszustand ist derjenige, in dem sich das erste Qubit im Zustand ∣+⟩|+\rangle befindet, während die anderen gepaart und im Bell-Zustand (∣00⟩+∣11⟩)/2(|00\rangle + |11\rangle)/\sqrt{2} gesetzt sind.

Die Größe, die wir beobachten wollen, ist die Korrelationsfunktion. Das Referenzpapier erörtert, wie diese Größe als ein Pauli-Operator X^\hat{X} auf dem Qubit nthn^{th} umgeschrieben werden kann. Nach einer Reihe von physikalischen Zeitschritten tt berechnen wir den Wert des Pauli-Operators X^n=t\hat{X}_{n=t}. Abhängig von den Parametern des Systems entspricht der Wert dieser Observablen einem Wert, der entweder exakt berechnet oder nur durch approximative Methoden simuliert werden kann. Konkret entspricht „ ∣J∣=∣b∣=π/4|J|=|b|=\pi/4 “ dem Wert „ [cos⁡(2h)]t[\cos(2h)]^t “, den wir zum Vergleich der Ergebnisse dieses Tutorials verwenden werden. Außerdem ist zu einem bestimmten Zeitschritt tt, ⟨X^n≠t⟩\langle\hat{X}_{n\neq t}\rangle gleich Null. Einzelheiten zur Ermittlung dieser Werte und zum Vergleich mit ungefähren klassischen Simulationsergebnissen außerhalb dieser Parameter finden Sie bei L. E. Fischer et al., Nat. Phys. (2026).

TEM funktioniert, indem zunächst das Rauschen für jede einzelne Schicht von Zwei-Qubit-Gates im Schaltkreis charakterisiert wird und anschließend der Auslesefehler charakterisiert wird. Anschließend wird der Schaltkreis auf der Quantenmaschine ausgeführt. Schließlich wird die Fehlerminderung des Tensornetzwerks auf den klassischen Ressourcen in „ IBM Cloud® “ durchgeführt und der geminderte Wert zurückgegeben. In diesem Beispiel hat die Schaltung zwei einzigartige Schichten, die charakterisiert werden müssen.


Einrichtung

Als Voraussetzung muss sichergestellt werden, dass die erforderlichen Abhängigkeiten installiert sind.

%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

Fehlerbehebung mit TEM

Wir stellen hier eine Schaltung vor, die das oben beschriebene Kicked-Ising-Modell implementiert. Die Schaltung wird wie folgt vorbereitet. Zunächst gibt es eine Zustandsvorbereitungsphase, in der sich das erste Qubit im Zustand ∣+⟩|+\rangle befindet, während sich die anderen in Bell-Paaren (∣00⟩+∣11⟩)/2(|00\rangle + |11\rangle)/\sqrt{2} befinden. Darauf folgt die Brickwork-Struktur, die die unitäre Entwicklung U^KI\hat{U}_{\text{KI}} implementiert. Die Anzahl der physikalischen Zeitschritte entspricht t/2t/2 den Schaltungsschichten.

Der folgende Code lädt die beiden für dieses Tutorial erforderlichen QASM-Dateien herunter.

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

Wir können uns eine kleine Version des Schaltkreises mit 12 Qubits und sechs Zeitschritten vorstellen:

# 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

Als Nächstes erstellen Sie die Beobachtungsgröße „ X^n=t\hat{X}_{n=t} “. Diese wird als einfache Pauli-Kette konstruiert, deren Reihenfolge mit der von Qiskit verwendeten übereinstimmt:

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)

In unserem kleinen 12-Qubit-Beispiel sieht die Beobachtungsgröße wie folgt aus:

# 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 Verwenden Sie PUBs als Mittel zum Sammeln der Eingaben. In unserem Fall betrachten wir einen einzelnen Schaltkreis und eine Beobachtung als unsere „ PUB “:

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

Als Nächstes erhalten wir Zugriff auf die TEM-Funktion. Zuerst richten wir die erforderliche Authentifizierung für IBM Cloud ein und wählen ein Backend aus den verfügbaren Geräten aus. Das Token, die verfügbaren Backends und die entsprechenden Cloud-Ressourcennamen (CRN) können Sie erhalten, indem Sie sich bei Ihrem Konto auf dem Dashboard „ IBM Quantum Platform “ anmelden.

# 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

Laden Sie die TEM-Funktion aus dem „ Qiskit Functions Catalog ” (Verzeichnis der TEM-Funktionen):

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

Wir können nun ein Experiment mit dem gekickten Ising-Schaltkreis durchführen, wobei TEM für die Fehlerminderung sorgt. Mit den Standardeinstellungen kann TEM auf einfache Weise ausgeführt werden, wobei die erwartete QPU-Laufzeit je nach QPU etwa 2.5 Minuten beträgt:

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

Mit den Standardoptionen führt die TEM-Funktion drei Aufgaben auf dem Quantencomputer aus: Rauschlernen, Auslesungsminderung und Schaltungsabtastung. Die Anzahl der von jedem dieser Elemente verwendeten Schüsse kann in den an die Funktion übergebenen Optionen geändert werden. Standardmäßig sind diese Parameter so eingestellt, dass eine Genauigkeit von 0.05 bei den geminderten Erwartungswerten erreicht wird.

Sie können den Status Ihres Auftrags auf dem Dashboard „ IBM Quantum Platform “ oder mit folgendem Befehl überprüfen:

print(tem_job.status())

Output:

QUEUED

Wenn der Status „“ ist DONE, können wir die Rohdaten und die gemilderten Ergebnisse überprüfen. Die unten tem_evs definierten Werte sind die Erwartungswerte der angeforderten Observablen, in diesem Fall nur eine Observable, ⟨X^n=t⟩\langle \hat X_{n=t}\rangle, und tem_std sind die entsprechenden Standardabweichungen.

# 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

Wir können auch überprüfen, wie viel Quantenlaufzeit für jeden Aufruf verwendet wurde, und zwar unter IBM Quantum Platform oder durch Überprüfen der Ergebnismetadaten aus dem Code 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

TEM-Parameter und erweiterte Optionen anpassen

Die TEM-Funktion bietet mehrere erweiterte Optionen zur Anpassung Ihres Workflows zur Fehlerbehebung. Mit diesen Optionen können Sie die Genauigkeit, die Anzahl der Schüsse, die Strategien zum Lernen von Rauschen und andere Parameter steuern, um sie besser an die Anforderungen Ihres Experiments und die verfügbaren Quantenressourcen anzupassen.

Häufige erweiterte Optionen sind:

  • precision: Geben Sie die Zielgenauigkeit für die gemilderten Erwartungswerte an.
  • **default_shots**Anstelle von können Sie die Anzahl der vom precisionMessauftrag verwendeten Aufnahmen angeben.
  • **tem_max_bond_dimension**Die maximale Bindungsdimension, die im Tensornetzwerk verwendet wird.
  • **tem_compression_cutoff**Der für das Tensornetzwerk zu verwendende Grenzwert.
  • Optionen für das Geräuschlernen : Konfigurieren Sie, wie Geräusche charakterisiert werden, z. B. die Anzahl der Wiederholungen oder bestimmte Kalibrierungsschaltungen.
  • **private**Stellen Sie sicher, dass Schaltkreise und Versuchsergebnisse für Sie privat bleiben, und deaktivieren Sie mehrere Downloads von Auftragsergebnissen.

Eine vollständige Liste der unterstützten Optionen und deren Beschreibungen finden Sie in der TEM-Dokumentation oder unter Qiskit Functions Catalog. Sie können diese Parameter anpassen, um ein Gleichgewicht zwischen Laufzeit, Ressourcenverbrauch und Genauigkeit der Ergebnisse herzustellen.

Sie können diese Optionen als Wörterbuch an das options Argument übergeben, wenn Sie die TEM-Funktion ausführen:

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

Es können auch benutzerdefinierte Optionen für den Noise-Learner übergeben werden. Sie folgen den Definitionen, die im verwendet werden: 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_options

Wiederholen Sie das Experiment mit diesen speziell auf unsere Schaltung abgestimmten Optionen. Die erwartete Laufzeit beträgt etwa vier QPU-Minuten.

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

Wenn der Auftrag nicht als privat gekennzeichnet ist, können wir das Ergebnis zu einem späteren Zeitpunkt wiederherstellen. Speichern Sie dazu die hier angegebene Auftrags-ID und verwenden Sie 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

Wir können nun die Ergebnisse und Metadaten überprüfen, um Einblicke in das Experiment zu gewinnen:

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

Schließlich können wir die Auswirkungen der benutzerdefinierten Optionen auf die QPU und die klassische Laufzeit überprüfen:

# 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

TEM auf große Schaltkreise skalieren

Große Schaltungen können grundsätzlich mit der TEM-Funktion betrieben werden. Es ist jedoch wichtig, sich der Grenzen der klassischen Ressourcen bewusst zu sein, da TEM auf IBM Cloud -Runnern mit potenziell sehr langen Laufzeiten ausgeführt wird. Bei extrem großen Schaltkreisen wenden Sie sich bitte an das TEM-Supportteam unter qiskit\ [email protected].

Hier führen wir ein Beispiel mit einer größeren Schaltung im Versorgungsmaßstab mit 30 Qubits durch, wobei die TEM-Parameter eher auf Geschwindigkeit als auf Genauigkeit optimiert werden.

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

Lassen Sie uns einige leistungsorientierte Optionen definieren:

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

Führen Sie schließlich das Experiment durch, erhalten Sie das Ergebnis und visualisieren Sie es. Dies dauert ungefähr 3.5 QPU-Minuten.

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
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.