Eine Qiskit-Funktionsvorlage für AQC + Trotter Hamiltonian Dynamics bereitstellen und ausführen
Überblick
Dies ist eine experimentunabhängige Qiskit-Funktionsvorlage für Hamiltonian Dynamics. Ausgehend von einem eindimensionalen Nächste-Nachbar-Pauli-Hamiltonian, einem vorbereiteten Anfangszustand (optional) und einer Reihe von Observablen führt sie Trotter-Zeitentwicklung, Schaltkreiskompression durch Approximate Quantum Compilation (AQC) und fehlerminimierte Ausführung durch und gibt dann die Zeitreihe jeder Observable zurück. Tausche das Setup (PRE) und die Analyse (POST) aus, und derselbe Kern treibt ein anderes Experiment an:
| PRE (dein Setup) | FUNCTION (hier bereitgestellt) | POST (deine Analyse) |
|---|---|---|
| Bereite einen Zustand vor, als Circuit oder Produktzustand, mit einem optionalen lokalen Kick | Trotter-Synthese → AQC-Kompression → Ausführung auf statevector, fake oder runtime, gibt zurück | für Neutronenstreuung, oder Magnetisierung, Transport, Quench-Dynamik und so weiter |
Die Vorlage wird im Qiskit-Function-Templates-Repository zusammen mit den anderen Anwendungsvorlagen veröffentlicht. Dieses Notebook stellt sie in deinem eigenen Qiskit Serverless-Konto bereit. Führe es einmal aus, und jedes Notebook kann die Funktion dann mit serverless.load("aqc-dynamics-function") aufrufen.
Ein ausgearbeitetes wissenschaftliches Beispiel findest du unter Neutronenstreuung mit einem AQC + Trotter-Dynamics-Serverless-Workflow simulieren, das diese Funktion aufruft, um den dynamischen Strukturfaktor von KCuF zu berechnen. Dieses Notebook behandelt stattdessen die Bereitstellung und den Eingabevertrag.
Voraussetzungen
Stelle vor dem Start sicher, dass du Folgendes in der Kernel-Umgebung dieses Notebooks hast:
-
Qiskit SDK v2.0 oder höher (
pip install qiskit). -
Der Qiskit IBM Catalog-Client (
pip install qiskit-ibm-catalog), der Workloads auf Qiskit Serverless bereitstellt und ausführt.
Die eigenen wissenschaftlichen Abhängigkeiten der Funktion (qiskit-addon-aqc-tensor, cotengrust, qiskit-aer) müssen nicht lokal installiert werden.
Die Quelldateien der Vorlage abrufen
Die Funktion ist ein kleines Python-Paket, das Qiskit Serverless in der Cloud ausführt. Daher muss der Quellcode als lokale Dateien vorliegen, die zum Zeitpunkt der Bereitstellung hochgeladen werden. Das Paket wird im Qiskit-Function-Templates-Repository veröffentlicht.
Lade source_files herunter
Der Download ist eine einzelne ZIP-Datei, benannt nach dem vollständigen Pfad des Verzeichnisses im Repository:
qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip
-
Entpacke sie in das Verzeichnis, das dieses Notebook enthält.
-
Benenne den entpackten Ordner von diesem langen Namen in
source_filesum.
Dein Arbeitsverzeichnis sieht dann so aus:
your-working-directory/
├── function-template-aqc-trotter.ipynb <- this notebook
└── source_files/ <- the renamed folder
├── __init__.py
├── program.py
└── source/
├── __init__.py
├── _serverless.py
├── app_function.py
├── aqc.py
├── build.py
├── execute.py
└── hamiltonian.py
Der Name muss genau source_files lauten, da dies das working_dir ist, das Schritt 3 hochlädt.
program.py ist der Einstiegspunkt, den das Gateway aufruft. Alles unter source/ ist die Implementierung, aufgeteilt nach Phase: Hamiltonian- und Trotter-Synthese, AQC-Kompression und Ausführung. Nichts davon muss bearbeitet werden, um die folgenden Beispiele auszuführen. Schritt 3 lädt das gesamte Verzeichnis hoch, wiederhole diesen Schritt also, wann immer du eine Datei änderst.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog
1. Authentifizierung
Verwende qiskit-ibm-catalog, um dich bei QiskitServerless mit deinem API-Schlüssel (Token) und deiner CRN (Instanz) zu authentifizieren, die du im IBM Quantum® Platform-Dashboard findest. Mit diesen Anmeldedaten kannst du den Serverless-Client lokal instanziieren, um die ausgewählte Funktion hochzuladen oder auszuführen:
from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
Optional kannst du save_account() verwenden, um deine Anmeldedaten in deiner lokalen Umgebung zu speichern (siehe den Leitfaden Richte dein IBM Cloud®-Konto ein). Beachte, dass dies deine Anmeldedaten in dieselbe Datei schreibt wie QiskitRuntimeService.save_account():
QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
Wenn das Konto gespeichert ist, muss zur Authentifizierung kein Token angegeben werden:
from qiskit_ibm_catalog import QiskitServerless
# Authenticate to the remote cluster
# In this case, loading a saved account
serverless = QiskitServerless()
# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT
# serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")
2. Abhängigkeiten deklarieren
Pakete, die die Funktion zusätzlich zum verwalteten Basis-Serverless-Image benötigt.
Das Gateway installiert nur Namen aus seiner Allowlist (requirements-dynamic-dependencies.txt), die anhand des Paketnamens abgeglichen und mit == auf die zulässige Version festgelegt werden. Alles andere muss transitiv ankommen (als Abhängigkeit eines Pakets aus der Allowlist). Die [extras]-Syntax wird berücksichtigt: qiskit-addon-aqc-tensor[quimb-jax] ist das, was quimb und jax installiert. cotengrust wird für die Speichereffizienz während der Tensor-Netzwerk-Simulation benötigt. qiskit-aer wird separat für das fake-Backend (lokale rauschbehaftete Simulation) aufgeführt.
DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]
3. Die Funktion definieren und hochladen
from qiskit_ibm_catalog import QiskitFunction
fn = QiskitFunction(
title="aqc-dynamics-function",
entrypoint="program.py",
working_dir="source_files/",
dependencies=DEPENDENCIES,
)
serverless.upload(fn)
QiskitFunction(aqc-dynamics-function)
4. Überprüfen, ob sie registriert wurde
next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)
Funktionsreferenz
Dies ist eine kurze Einführung. Jedes Feld ist vollständig im AQC Dynamics Template README dokumentiert: die vollständige Eingabetabelle mit ihren Validierungsregeln, die Ausgabefelder, die Ausführungs-Backends und weitere ausgearbeitete Beispiele. Im Folgenden findest du die Kurzversion, die ausreicht, um die nachfolgenden Beispiele zu lesen.
Eingaben
Jeder Lauf ist ein einzelner fn.run(...)-Aufruf. Nur die ersten drei Eingaben in der Tabelle sind erforderlich: hamiltonian, t_steps und aqc_segments. Alles danach ist optional und greift auf den gezeigten Standardwert zurück, sodass ein minimaler Aufruf drei Argumente umfasst und der Rest der Tabelle die Funktionalität ist, die du optional nutzen kannst. num_qubits des Hamiltonian legt die Kettenlänge fest, daher gibt es keine separate Eingabe für die Größe.
| Input | Standard | Beschreibung |
|---|---|---|
hamiltonian | erforderlich | 1D-Pauli-Hamiltonoperator mit Nächste-Nachbarn-Wechselwirkung als SparsePauliOp. Strings sind Pauli-Operatoren, daher gibt es keinen impliziten Faktor von einem Halb. |
t_steps | erforderlich | Gesamtzahl der Trotter-Schritte. Entwickelt sich bis T = t_steps * dt und meldet jede Observable bei jedem t_k = k * dt. |
aqc_segments | erforderlich | Kompressionsplan: eine Liste von {"n_steps": k, "ansatz_steps": m}. sum(n_steps) Schritte werden komprimiert; der Rest läuft als reines Trotter. |
dt | 0.2 | Physikalische Zeit, die pro Trotter-Schritt fortschreitet. |
initial_state | |0...0> | Ein vorbereiteter QuantumCircuit, der entwickelt werden soll. Baue jeden lokalen Kick in diesen Circuit ein. |
observables | pro Site Z | Alles, was EstimatorV2 als sein observables-Argument akzeptiert. Eine Observable pro Ausgabespalte. |
trotter_options | Suzuki 2. Ordnung | {"method": ..., "synthesis_settings": {...}}. reps und time werden von der Funktion selbst gesetzt. |
aqc_options | siehe Beschreibung | max_bond (32), cutoff (1e-8), autodiff_backend ("jax"), fidelity_target (None), optimizer_settings (L-BFGS-B, jac=True, maxiter=300). |
estimator_options | DD, Twirling, TREX | EstimatorV2.options, unverändert weitergereicht. Ein übergebenes Dictionary ersetzt die Standardwerte vollständig, statt mit ihnen zusammengeführt zu werden. |
transpiler_options | {"optimization_level": 3} | Schlüsselwortargumente für generate_preset_pass_manager. backend und target werden abgelehnt, da der Ausführungspfad sie selbst verwaltet. |
backend | "runtime" | "statevector", "fake" oder "runtime". |
backend_name | am wenigsten ausgelastet | IBM®-Backend-Name für runtime, oder ein benanntes Fake-Backend. |
batches | 1 | Teilt die Circuits auf N Runtime-Jobs auf. Ein Batch reicht einen einzelnen Job ein und erstellt keine Session. |
parallel_sim | False | Verteilt die lokalen Simulatorpfade mit Ray auf alle verfügbaren Kerne. Hat keine Auswirkung auf runtime. |
return_circuits | False | Gibt die logischen AQC- + Trotter-Circuits zusammen mit der Observablen-Reihe im Ergebnis zurück. |
Ausführungs-Backends
Alle drei Pfade verwenden denselben Code und dieselben Mitigations-Einstellungen. Sie unterscheiden sich nur darin, wo die Circuits ausgeführt werden.
backend | Was es ist | Anmeldedaten | Hinweise |
|---|---|---|---|
"statevector" | Exakter StatevectorEstimator | Nur Serverless-Konto | Der exakte Referenzpfad. Keine QPU-Zeit. |
"fake" | Verrauschte lokale Simulation auf einem Qiskit-Fake-Backend | Nur Serverless-Konto | Eine originalgetreue Generalprobe des mitigierten runtime-Pfads. Benötigt qiskit-aer. Standardmäßig das 127-Qubit-fake_sherbrooke. |
"runtime" (Standard) | Der mitigierte EstimatorV2 gegen eine echte QPU | Serverless-Konto und eine Instanz mit QPU-Zugriff | backend_name optional; wird es weggelassen, wählt die Funktion das am wenigsten ausgelastete Gerät. |
Beide Simulatorpfade rufen weiterhin die bereitgestellte Funktion auf, weshalb sie ein gespeichertes Serverless-Konto benötigen, obwohl sie keine QPU-Zeit verbrauchen. Die beiden folgenden Beispiele führen dieselbe Workload zunächst auf statevector und dann auf runtime aus.
Ausgabe
job.result() gibt ein einfaches Dictionary zurück:
{
"times": [...], # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)
"expectation_values": [[...]], # shape (n_times, n_observables)
"observable_labels": [...], # for example: ["Z_0", "ZZ_0_1"]
"metadata": {
"n", "t_steps", "dt", "tier",
"aqc_compressed_steps": 5, # total compressed steps (= sum of segment n_steps)
"aqc_segments": [ # per segment: the plan plus its own results
{"n_steps": 3, "ansatz_steps": 1, "steps": [1, 2, 3], "n_params": 133,
"fidelities": {"1": ..., "2": ..., "3": ...}},
{"n_steps": 2, "ansatz_steps": 2, "steps": [4, 5], "n_params": 245,
"fidelities": {"4": ..., "5": ...}},
],
"execution_backend",
"aqc_fidelities": {"1": ..., "2": ...}, # flat per-step fidelity, all compressed steps
"circuit_stats": { # per-step 2q depth and gate count, full Trotter vs AQC
"1": {"full_trotter": {"depth_2q": ..., "num_2q_gates": ...},
"aqc_trotter": {"depth_2q": ..., "num_2q_gates": ...}},
"2": {...},
},
"warnings": [...], # non-fatal notices; for example, a cotengrust fallback
"resource_usage": { # per stage; QPU_TIME is the charged QPU time
"RUNNING: OPTIMIZING_FOR_HARDWARE": {"CPU_TIME": ...},
"RUNNING: WAITING_FOR_QPU": {"CPU_TIME": ...},
"RUNNING: EXECUTING_QPU": {"QPU_TIME": ...},
},
},
# present only when return_circuits=True
"circuits": [QuantumCircuit, ...], # one per evolved step; circuits[i] is at times[i + 1]
}
aqc_fidelities und circuit_stats solltest du zuerst lesen: Zusammen zeigen sie dir, ob die Kompression originalgetreu geblieben ist und ob sie tatsächlich Tiefe eingespart hat. Bei runtime meldet resource_usage die Wartezeit in der Warteschlange getrennt von der QPU-Zeit, die dir berechnet wird. Eine abgelehnte Eingabe schlägt sofort mit einem strukturierten ServerlessError (Code 4615) fehl.
Simulator-Beispiel
Führe die Funktion zuerst auf dem exakten statevector-Backend aus. Sie verbraucht keine QPU-Zeit und validiert die Bereitstellung durchgängig. Das Modell hier ist eine Acht-Qubit-Ising-Kette mit transversalem Feld, und observables wird weggelassen, sodass die Funktion das Standard- pro Site misst.
Der Kompressionsplan ist die Eingabe, die es zu verstehen lohnt. Jedes Segment {"n_steps": k, "ansatz_steps": m} komprimiert k aufeinanderfolgende Trotter-Schritte zu einem Ansatz, der aus einem m-Schritt-Trotter-Ziel aufgebaut ist, und alle Schritte über sum(n_steps) hinaus laufen als reines Trotter. Frühe Schritte mit geringer Verschränkung lassen sich gut zu einem flachen Einzelschicht-Ansatz komprimieren; spätere, stärker verschränkte Schritte benötigen einen tieferen.
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 8
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=8,
aqc_segments=[
{
"n_steps": 4,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 2,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="statevector",
)
print("job ID:", job.job_id)
job ID: ee1f3793-e995-427d-81d1-5924549beb38
Den Lauf verfolgen und das Ergebnis lesen
status() meldet sowohl den groben Job-Lebenszyklus als auch den Sub-Status pro Phase, den die Funktion während der Ausführung veröffentlicht. Dieselben Phasen gelten für den Hardware-Lauf später in diesem Guide:
QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE
status()-Wert | Phase |
|---|---|
RUNNING: OPTIMIZING_FOR_HARDWARE | Zustandsvorbereitung, Trotter-Aufbau, AQC-Kompression |
RUNNING: WAITING_FOR_QPU | wartet in der QPU-Warteschlange (nur runtime-Backend) |
RUNNING: EXECUTING_QPU | Circuits werden ausgeführt (lokale Simulatoren markieren dies direkt) |
RUNNING: POST_PROCESSING | Zusammenstellung des Ergebnis-Dictionarys |
Endzustände sind DONE, ERROR und CANCELED. Dieser statevector-Lauf hat keine QPU-Warteschlange und überspringt daher RUNNING: WAITING_FOR_QPU. Verwende jederzeit job.logs(), um die Logs pro Phase einzusehen, einschließlich der bei jedem Schritt erreichten AQC-Fidelity.
print(job.status()) # re-run until this reports DONE
DONE
import numpy as np
result = job.result()
ev = np.array(result["expectation_values"])
print("observables:", result["observable_labels"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("first row (t = 0, the prepared state):", np.round(ev[0], 4))
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']
shape: (9, 8) -> (n_times, n_observables)
first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]
last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}
2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)
Hardware-Beispiel
Ein Funktionsaufruf mit backend="runtime" transpiliert die Circuits und führt sie auf einem echten IBM Quantum-Prozessor aus, mit der in die Funktion eingebauten Fehlerminderung: Dynamical Decoupling (XY4), Gate Twirling und Twirled Readout Error Extinction (TREX). backend_name wählt das Gerät aus; wird es weggelassen, nimmt die Funktion das am wenigsten ausgelastete.
Am wissenschaftlichen Code ändert sich nichts. Was sich gegenüber dem Simulator-Beispiel unterscheidet, sind die Kettenlänge, die Anzahl der Trotter-Schritte, der Kompressionsplan, das Backend und die expliziten Mitigations-Einstellungen, die im folgenden Abschnitt behandelt werden.
Den Job für die Steuerhardware dimensionieren
estimator_options ist die Eingabe, die es lohnt, bewusst zu setzen. Gate Twirling erzeugt für jeden PUB num_randomizations separate randomisierte Circuits, und der gesamte Job – jeder PUB mit all seinen Randomisierungen – muss in den Instruktionsspeicher des klassischen Steuersystems der QPU passen. Die Funktion verwendet standardmäßig 1000 Randomisierungen, sodass eine 10-Schritt-Entwicklung 11 PUBs mit je 1000 Circuits einreicht: rund 11.000 Circuit-Instanzen in einem einzigen Job.
Überschreitest du, was das Steuersystem fasst, schlägt der Job mit Fehler 6073 fehl. Job-Limits nennt die Schwellenwerte und wie man dagegen zählt, wobei der wichtigste 26,8 Millionen Steuersystem-Instruktionen pro Qubit ist, angewendet pro Job statt pro PUB. Dynamical Decoupling fügt Gates hinzu, die dagegen zählen.
Zwei Eingaben steuern die Größe:
-
estimator_optionslegt das Shot-Budget fest. Die Gesamtzahl der Shots istnum_randomizations * shots_per_randomization, sodass du Randomisierungen gegen Shots pro Randomisierung eintauschen, die Statistik beibehalten und das Programm dennoch verkleinern kannst. Die folgende Zelle verwendet 100 Randomisierungen mit je 200 Shots, was 20.000 Shots pro Observable ergibt und etwa ein Zehntel der Circuit-Instanzen, die die Standardwerte einreichen würden. Die vollständige Liste der Felder findest du unter TwirlingOptions und Estimator-Optionen. -
batchesteilt die PUBs auf so viele separate Runtime-Jobs auf, was genau die Abhilfe ist, die Fehler 6073 selbst vorschlägt, und warum die Pro-Job-Betrachtung wichtig ist.batches=4sendet statt elf auf einmal etwa drei PUBs pro Job, und die Jobs gehen zusammen in einem Batch heraus, sodass die Gruppe einmal in der Warteschlange landet statt jeder Job separat.
Denk daran, dass ein übergebenes estimator_options die Standardwerte der Funktion vollständig ersetzt, statt mit ihnen zusammengeführt zu werden. Deshalb werden Dynamical Decoupling und TREX in der folgenden Zelle erneut angegeben, damit sie eingeschaltet bleiben.
from qiskit.quantum_info import SparsePauliOp
fn = serverless.load("aqc-dynamics-function")
n = 10
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)
job = fn.run(
t_steps=10,
aqc_segments=[
{
"n_steps": 3,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 3,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="runtime",
backend_name="ibm_marrakesh",
# The function defaults to 1000 twirling randomizations, which was too large
# for this device. Total shots is num_randomizations *
# shots_per_randomization, so this is 20,000 shots per observable.
estimator_options={
"dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
"twirling": {
"enable_gates": True,
"num_randomizations": 100,
"shots_per_randomization": 200,
},
"resilience": {"measure_mitigation": True},
},
)
print("job ID (save this to reconnect later):", job.job_id)
job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9
Ein Hardware-Lauf ist nicht schnell, und der größte Teil der Zeit ist klassisch statt auf der QPU. Die AQC-Kompression läuft innerhalb der Funktion, bevor überhaupt etwas die QPU erreicht, und die QPU-Warteschlange kommt noch hinzu. Du musst dieses Notebook oder den Kernel während der Ausführung nicht geöffnet halten.
Kopiere die von der vorherigen Zelle ausgegebene Job-ID und speichere sie. Mit den nächsten drei Zellen kannst du den Lauf später wieder aufnehmen:
-
Wiederverbinden, nur in einer neuen Kernel-Session nötig: Führe die Zelle Authentifizierung erneut aus, um
serverlessneu zu erstellen, und baue dann dasjob-Handle aus der gespeicherten ID wieder auf. Überspringe diese Zelle, wenn du dich noch in der Session befindest, in der du eingereicht hast, da das Handle bereits aktiv ist. -
Status prüfen: erneut ausführen, bis
DONEgemeldet wird. -
Ergebnis abrufen: erst ausführen, sobald der Status
DONEist.
Füge deine gespeicherte ID anstelle des Platzhalters in der folgenden Wiederverbindungs-Zelle ein.
# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Re-run this until it reports DONE, then fetch the result in the following cell.
print(job.status())
DONE
import numpy as np
# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits.
result = job.result()
ev = np.array(result["expectation_values"])
print("backend:", result["metadata"]["execution_backend"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)
# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
backend: runtime
shape: (11, 10) -> (n_times, n_observables)
last row (t = t_steps * dt): [0.1504 0.1361 0.218 0.2144 0.2275 0.1783 0.1749 0.1599 0.0915 0.0922]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}
2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)
Nächste Schritte
-
Arbeite Simulate neutron scattering with an AQC + Trotter dynamics Serverless workflow durch, das begleitende Beispiel, das diese bereitgestellte Funktion aufruft, um den dynamischen Strukturfaktor von KCuF zu berechnen.
-
Lies die AQC Dynamics Template auf GitHub für den vollständigen Eingabe- und Ausgabevertrag, weitere Beispiele und Zitierdetails.
-
Durchstöbere das Qiskit-Function-Templates-Repository nach weiteren Anwendungsvorlagen, die auf dieselbe Weise aufgebaut sind.
-
Lies den Qiskit Serverless-Guide zur Verwaltung bereitgestellter Funktionen.
-
Vertiefe die AQC-Kompressionsphase mit der Qiskit Addon: AQC-Tensor-Dokumentation.