{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Bereitstellung und Ausführung einer Qiskit-Funktionsvorlage für AQC + Trotter-Hamilton-Dynamik\"\n",
        "description: \"Implementieren Sie die Vorlage für die AQC- und Trotter-Hamilton-Dynamik in „ Qiskit Serverless “ und führen Sie sie anschließend auf einem Simulator und auf einer QPU aus.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Trotter Trotterization quimb cotengra cotengrust Suzuki fidelities isa */}\n",
        "\n",
        "<span id=\"deploy-and-run-a-qiskit-function-template-for-aqc-+-trotter-hamiltonian-dynamics\" />\n",
        "\n",
        "# Bereitstellung und Ausführung einer Qiskit-Funktionsvorlage für AQC + Trotter-Hamilton-Dynamik\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "version-info",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "overview",
      "metadata": {},
      "source": [
        "<span id=\"overview\" />\n",
        "\n",
        "## Übersicht\n",
        "\n",
        "Dies ist eine experimentunabhängige Qiskit-Funktionsvorlage für die Hamilton-Dynamik. Ausgehend von einem „ 1D “-Pauli-Hamiltonian nach der Nearest-Neighbor-Methode, einem vorbereiteten Anfangszustand (optional) und einer Menge von Observablen führt das Programm eine Trotter-Zeitentwicklung, eine Schaltkreiskompression mittels approximativer Quantenkompilierung (AQC) sowie eine mitigierte Ausführung durch und gibt anschließend die Zeitreihen der einzelnen Observablen zurück. Tauscht man die Versuchsanordnung (PRE) und die Auswertung (POST) aus, führt derselbe Kern zu einem anderen Experiment:\n",
        "\n",
        "| PRE (Ihre Konfiguration)                                                                                 | FUNKTION (hier bereitgestellt)                                                                                                               | POST (deine Analyse)                                                                              |\n",
        "| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |\n",
        "| Bereiten Sie einen Zustand vor – als Schaltkreis oder Produktzustand – mit einem optionalen lokalen Kick | Trotter-Synthese → AQC-Komprimierung → Ausführung auf `statevector`, `fake`, oder `runtime`, mit der Rückgabe von „ $\\langle O \\rangle(t)$ “ | $S(q, \\omega)$ für Neutronenstreuung oder Magnetisierung, Transport, Quench-Dynamik und so weiter |\n",
        "\n",
        "Die Vorlage ist im [Qiskit-Repository für Funktionsvorlagen](https://github.com/qiskit-community/qiskit-function-templates/tree/main/physics/aqc_trotter) zusammen mit den anderen Anwendungsvorlagen veröffentlicht. Dieses Notebook stellt es in Ihrem eigenen „ Qiskit Serverless “-Konto bereit. Führen Sie den Code einmal aus, dann kann jedes Notebook die Funktion mit aufrufen `serverless.load(\"aqc-dynamics-function\")`.\n",
        "\n",
        "Ein konkretes wissenschaftliches Beispiel finden Sie unter [Simulation der Neutronenstreuung mit einem AQC + Trotter-Dynamik-Workflow ohne Server](/docs/tutorials/simulate-neutron-scattering-with-a-serverless-workflow), in dem diese Funktion aufgerufen wird, um den dynamischen Strukturfaktor von KCuF$_3$ zu berechnen. Dieses Notebook befasst sich stattdessen mit der Bereitstellung und dem Eingabekontrakt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Anforderungen\n",
        "\n",
        "Bevor Sie beginnen, vergewissern Sie sich, dass in der Kernel-Umgebung dieses Notebooks Folgendes vorhanden ist:\n",
        "\n",
        "* Qiskit SDK v2.0 oder höher (`pip install qiskit`).\n",
        "* Der Qiskit- IBM -Katalog-Client (`pip install qiskit-ibm-catalog`), der Workloads auf Qiskit Serverless bereitstellt und ausführt.\n",
        "\n",
        "Die funktionsspezifischen wissenschaftlichen Abhängigkeiten (`qiskit-addon-aqc-tensor`, `cotengrust`, `qiskit-aer`) müssen nicht lokal installiert werden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "source-files",
      "metadata": {},
      "source": [
        "<span id=\"get-the-template-source-files\" />\n",
        "\n",
        "## Laden Sie die Quelldateien der Vorlage herunter\n",
        "\n",
        "Bei der Funktion handelt es sich um ein kleines „ Python “-Paket, das unter Qiskit Serverless in der Cloud ausgeführt wird. Daher muss der Quellcode als lokale Dateien vorliegen, die zum Zeitpunkt der Bereitstellung hochgeladen werden. Das Paket ist im Repository „Qiskit Function templates“ veröffentlicht.\n",
        "\n",
        "Herunterladen **[`source_files`](https://download-directory.github.io/?url=https%3A%2F%2Fgithub.com%2Fqiskit-community%2Fqiskit-function-templates%2Ftree%2Fmain%2Fphysics%2Faqc_trotter%2Fsource_files)**\n",
        "\n",
        "Der Download besteht aus einer einzigen ZIP-Datei, deren Name sich nach dem vollständigen Pfad des Verzeichnisses im Repository richtet:\n",
        "\n",
        "`qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip`\n",
        "\n",
        "1. Entpacken Sie die Datei in das Verzeichnis, in dem sich dieses Notizbuch befindet.\n",
        "2. Benenne den entpackten Ordner von diesem langen Namen in `source_files`. um.\n",
        "\n",
        "Ihr Arbeitsverzeichnis sieht dann wie folgt aus:\n",
        "\n",
        "```\n",
        "your-working-directory/\n",
        "├── function-template-aqc-trotter.ipynb    <- this notebook\n",
        "└── source_files/                         <- the renamed folder\n",
        "    ├── __init__.py\n",
        "    ├── program.py\n",
        "    └── source/\n",
        "        ├── __init__.py\n",
        "        ├── _serverless.py\n",
        "        ├── app_function.py\n",
        "        ├── aqc.py\n",
        "        ├── build.py\n",
        "        ├── execute.py\n",
        "        └── hamiltonian.py\n",
        "```\n",
        "\n",
        "Der Name muss genau so lauten `source_files`, denn das sind die Uploads aus Schritt `working_dir` 3.\n",
        "\n",
        "`program.py` ist der Einstiegspunkt, den das Gateway aufruft. Alles unter `source/` ist die Implementierung, unterteilt nach Phasen: Hamilton- und Trotter-Synthese, AQC-Komprimierung und Ausführung. Es sind keinerlei Änderungen erforderlich, um die folgenden Beispiele auszuführen. In Schritt 3 wird das gesamte Verzeichnis hochgeladen. Wiederholen Sie diesen Schritt daher jedes Mal, wenn Sie eine Datei ändern.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "auth-md",
      "metadata": {},
      "source": [
        "<span id=\"1-authentication\" />\n",
        "\n",
        "## 1. Authentifizierung\n",
        "\n",
        "Verwenden Sie `qiskit-ibm-catalog` zur Authentifizierung bei `QiskitServerless` mit Ihrem API-Schlüssel (Token) und der „ CRN “ (Instanz), die Sie im Dashboard von „ [IBM Quantum® Platform]() “ finden. Mit diesen Anmeldedaten können Sie den serverlosen Client lokal instanziieren, um die ausgewählte Funktion hochzuladen oder auszuführen:\n",
        "\n",
        "```python\n",
        "from qiskit_ibm_catalog import QiskitServerless\n",
        "serverless = QiskitServerless(channel=\"ibm_quantum_platform\", token=\"MY_TOKEN\", instance=\"MY_CRN\")\n",
        "```\n",
        "\n",
        "Optional können Sie verwenden, `save_account()` um Ihre Anmeldedaten in Ihrer lokalen Umgebung zu speichern (siehe die Anleitung [„Einrichten Ihres IBM Cloud® -Kontos“](/docs/guides/cloud-setup#cloud-save) ). Beachten Sie, dass Ihre Anmeldedaten dabei in dieselbe Datei geschrieben werden wie bei [`QiskitRuntimeService.save_account()`](/docs/api/qiskit-ibm-runtime/qiskit-runtime-service#save_account):\n",
        "\n",
        "```python\n",
        "QiskitServerless.save_account(channel=\"ibm_quantum_platform\", token=\"MY_TOKEN\", instance=\"MY_CRN\")\n",
        "```\n",
        "\n",
        "Wenn das Konto gespeichert ist, muss zur Authentifizierung kein Token angegeben werden:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 76,
      "id": "auth-code",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_catalog import QiskitServerless\n",
        "\n",
        "# Authenticate to the remote cluster\n",
        "# In this case, loading a saved account\n",
        "serverless = QiskitServerless()\n",
        "\n",
        "# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT\n",
        "# serverless = QiskitServerless(channel=\"ibm_quantum_platform\", token=\"MY_TOKEN\", instance=\"MY_CRN\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "deps-md",
      "metadata": {},
      "source": [
        "<span id=\"2-declare-dependencies\" />\n",
        "\n",
        "## 2. Abhängigkeiten deklarieren\n",
        "\n",
        "Pakete, die die Funktion zusätzlich zum verwalteten Basis-Serverless-Image benötigt.\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Das Gateway installiert nur Namen, die in seiner Zulassungsliste ([`requirements-dynamic-dependencies.txt`](https://github.com/Qiskit/qiskit-serverless/blob/main/docker-images/requirements-dynamic-dependencies.txt)) aufgeführt sind, die anhand des Paketnamens abgeglichen und mit der zulässigen Version verknüpft sind `==`. Alles andere muss transitiv bereitgestellt werden (als Abhängigkeit eines Pakets, das auf der Zulassungsliste steht). Die Syntax `[extras]` wird berücksichtigt: Damit werden `qiskit-addon-aqc-tensor[quimb-jax]` und `quimb` installiert `jax`. `cotengrust` wird für eine effiziente Speichernutzung bei der Simulation von Tensornetzwerken benötigt. `qiskit-aer` wird für das `fake` Backend (lokale Simulation mit Rauschen) separat aufgeführt.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "deps-code",
      "metadata": {},
      "outputs": [],
      "source": [
        "DEPENDENCIES = [\n",
        "    \"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1\",\n",
        "    \"qiskit-aer==0.17.2\",\n",
        "    \"cotengrust==0.2.0\",\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "upload-md",
      "metadata": {},
      "source": [
        "<span id=\"3-define-and-upload-the-function\" />\n",
        "\n",
        "## 3. Die Funktion definieren und hochladen\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "upload-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "QiskitFunction(aqc-dynamics-function)"
            ]
          },
          "execution_count": 119,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_catalog import QiskitFunction\n",
        "\n",
        "fn = QiskitFunction(\n",
        "    title=\"aqc-dynamics-function\",\n",
        "    entrypoint=\"program.py\",\n",
        "    working_dir=\"source_files/\",\n",
        "    dependencies=DEPENDENCIES,\n",
        ")\n",
        "serverless.upload(fn)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "verify-md",
      "metadata": {},
      "source": [
        "<span id=\"4-verify-it-registered\" />\n",
        "\n",
        "## 4. Überprüfen Sie, ob es registriert ist\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "verify-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "QiskitFunction(aqc-dynamics-function)"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "next(p for p in serverless.list() if p.title == \"aqc-dynamics-function\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "reference-md",
      "metadata": {},
      "source": [
        "<span id=\"function-reference\" />\n",
        "\n",
        "## Funktionsübersicht\n",
        "\n",
        "Dies ist eine kurze Einführung. Jedes Feld ist in der [README-Datei](https://github.com/qiskit-community/qiskit-function-templates/blob/main/physics/aqc_trotter/README.md) zur AQC Dynamics-Vorlage ausführlich dokumentiert: die vollständige Eingabetabelle mit ihren Validierungsregeln, die Ausgabefelder, die Ausführungs-Backends sowie weitere ausgearbeitete Beispiele. Was nun folgt, ist die Kurzfassung – ausreichend, um die nachfolgenden Beispiele zu verstehen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "inputs-md",
      "metadata": {},
      "source": [
        "<span id=\"inputs\" />\n",
        "\n",
        "### Eingaben\n",
        "\n",
        "Jeder Durchlauf ist ein einzelner Aufruf `fn.run(...)` . Nur die ersten drei Einträge in der Tabelle sind erforderlich: `hamiltonian`, `t_steps`, und `aqc_segments`. Alles, was danach folgt, ist optional und wird auf die angezeigte Standardeinstellung zurückgesetzt. Ein minimaler Aufruf umfasst also drei Argumente, und der Rest der Tabelle enthält die Funktionen, die Sie optional nutzen können. Der Hamilton-Operator `num_qubits` legt die Kettenlänge fest, daher gibt es keine separate Eingabe für die Größe.\n",
        "\n",
        "| Eingabe              | Standard                    | Beschreibung                                                                                                                                                       |                                                                                                                         |\n",
        "| -------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |\n",
        "| `hamiltonian`        | erforderlich                | 1D Pauli-Hamiltonian des nächsten Nachbarn als `SparsePauliOp`. Strings sind Pauli-Operatoren, daher gibt es keinen impliziten Faktor von einem Halb.              |                                                                                                                         |\n",
        "| `t_steps`            | erforderlich                | Gesamtzahl der Trotter-Schritte. Entwickelt sich zu `T = t_steps * dt` und gibt bei jedem jede Beobachtungsgröße an `t_k = k * dt`.                                |                                                                                                                         |\n",
        "| `aqc_segments`       | erforderlich                | Komprimierungsplan: eine Liste von `{\"n_steps\": k, \"ansatz_steps\": m}`. `sum(n_steps)` Die Schritte sind komprimiert; der Rest läuft als normales Trotter.         |                                                                                                                         |\n",
        "| `dt`                 | `0.2`                       | Die physikalische Zeit wurde um einen Trotter-Schritt vorgerückt.                                                                                                  |                                                                                                                         |\n",
        "| `initial_state`      | \\`                          | 0...0>\\`                                                                                                                                                           | Ein, das bereit ist, sich `QuantumCircuit` weiterzuentwickeln. Bringen Sie in dieser Rennstrecke etwas Lokales mit ein. |\n",
        "| `observables`        | pro Standort `Z`            | Alles `EstimatorV2` akzeptiert als Argument `observables` . Ein Beobachtungswert pro Ausgabespalte.                                                                |                                                                                                                         |\n",
        "| `trotter_options`    | Suzuki zweiter Ordnung      | `{\"method\": ..., \"synthesis_settings\": {...}}`. und `reps` `time` gehören der Funktion.                                                                            |                                                                                                                         |\n",
        "| `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`).        |                                                                                                                         |\n",
        "| `estimator_options`  | DD, Wirbeln, TREX           | `EstimatorV2.options`, unverändert übernommen. Ein bereitgestelltes Wörterbuch ersetzt die Standardwörterbücher vollständig, anstatt sich mit ihnen zu vermischen. |                                                                                                                         |\n",
        "| `transpiler_options` | `{\"optimization_level\": 3}` | `generate_preset_pass_manager` Schlüsselwortargumente. `backend` und `target` werden verworfen, da sie zum Ausführungspfad gehören.                                |                                                                                                                         |\n",
        "| `backend`            | `\"runtime\"`                 | `\"statevector\"`, `\"fake\"`, oder `\"runtime\"`.                                                                                                                       |                                                                                                                         |\n",
        "| `backend_name`       | am wenigsten frequentiert   | IBM® Backend-Name für `runtime`… oder ein benanntes Schein-Backend.                                                                                                |                                                                                                                         |\n",
        "| `batches`            | `1`                         | Verteilen Sie die Schaltkreise auf N Laufzeit-Jobs. Bei einem Batch-Lauf wird ein einzelner Auftrag übermittelt, ohne dass eine Sitzung erstellt wird.             |                                                                                                                         |\n",
        "| `parallel_sim`       | `False`                     | Verteilen Sie die lokalen Simulatorpfade mit Ray auf alle verfügbaren Kerne. Keine Auswirkungen auf `runtime`.                                                     |                                                                                                                         |\n",
        "| `return_circuits`    | `False`                     | Geben Sie im Ergebnis neben der Beobachtungsreihe auch die logischen AQC- und Trotter-Schaltungen zurück.                                                          |                                                                                                                         |\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "backends-md",
      "metadata": {},
      "source": [
        "<span id=\"execution-backends\" />\n",
        "\n",
        "### Ausführungs-Backends\n",
        "\n",
        "Alle drei Pfade verwenden denselben Code und dieselben Einstellungen zur Risikominderung. Sie unterscheiden sich lediglich darin, wo die Schaltkreise verlaufen.\n",
        "\n",
        "| `backend`             | Was es ist                                                    | Berechtigungsnachweise                            | Hinweise                                                                                                                                          |\n",
        "| --------------------- | ------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n",
        "| `\"statevector\"`       | Exakt `StatevectorEstimator`                                  | Nur für Serverless-Konten                         | Der genaue Referenzpfad. Keine QPU-Zeit.                                                                                                          |\n",
        "| `\"fake\"`              | Lärmbehaftete lokale Simulation auf einem Qiskit-Fake-Backend | Nur für Serverless-Konten                         | Eine getreue Nachahmung des gemilderten `runtime` Weges. Bedarf `qiskit-aer`. Standardmäßig ist der 127-Qubit-Wert eingestellt `fake_sherbrooke`. |\n",
        "| `\"runtime\"`(Standard) | Die Gegenmaßnahmen `EstimatorV2` gegen eine echte QPU         | Serverless-Konto und eine Instanz mit QPU-Zugriff | `backend_name` optional; wird dieser Parameter weggelassen, wird das am wenigsten ausgelastete Gerät ausgewählt.                                  |\n",
        "\n",
        "Beide Simulatorpfade rufen weiterhin die bereitgestellte Funktion auf, daher benötigen sie ein gespeichertes Serverless-Konto, auch wenn sie keine QPU-Zeit beanspruchen. In den beiden folgenden Beispielen wird dieselbe Arbeitslast zunächst `statevector` auf und anschließend auf ausgeführt `runtime`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "output-md",
      "metadata": {},
      "source": [
        "<span id=\"output\" />\n",
        "\n",
        "### Ausgabe\n",
        "\n",
        "`job.result()` gibt ein einfaches Wörterbuch zurück:\n",
        "\n",
        "```python\n",
        "{\n",
        "    \"times\": [...],                  # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)\n",
        "    \"expectation_values\": [[...]],   # shape (n_times, n_observables)\n",
        "    \"observable_labels\": [...],      # for example: [\"Z_0\", \"ZZ_0_1\"]\n",
        "    \"metadata\": {\n",
        "        \"n\", \"t_steps\", \"dt\", \"tier\",\n",
        "        \"aqc_compressed_steps\": 5,   # total compressed steps (= sum of segment n_steps)\n",
        "        \"aqc_segments\": [            # per segment: the plan plus its own results\n",
        "            {\"n_steps\": 3, \"ansatz_steps\": 1, \"steps\": [1, 2, 3], \"n_params\": 133,\n",
        "             \"fidelities\": {\"1\": ..., \"2\": ..., \"3\": ...}},\n",
        "            {\"n_steps\": 2, \"ansatz_steps\": 2, \"steps\": [4, 5], \"n_params\": 245,\n",
        "             \"fidelities\": {\"4\": ..., \"5\": ...}},\n",
        "        ],\n",
        "        \"execution_backend\",\n",
        "        \"aqc_fidelities\": {\"1\": ..., \"2\": ...},  # flat per-step fidelity, all compressed steps\n",
        "        \"circuit_stats\": {           # per-step 2q depth and gate count, full Trotter vs AQC\n",
        "            \"1\": {\"full_trotter\": {\"depth_2q\": ..., \"num_2q_gates\": ...},\n",
        "                \"aqc_trotter\":  {\"depth_2q\": ..., \"num_2q_gates\": ...}},\n",
        "            \"2\": {...},\n",
        "        },\n",
        "        \"warnings\": [...],           # non-fatal notices; for example, a cotengrust fallback\n",
        "        \"resource_usage\": {          # per stage; QPU_TIME is the charged QPU time\n",
        "            \"RUNNING: OPTIMIZING_FOR_HARDWARE\": {\"CPU_TIME\": ...},\n",
        "            \"RUNNING: WAITING_FOR_QPU\": {\"CPU_TIME\": ...},\n",
        "            \"RUNNING: EXECUTING_QPU\": {\"QPU_TIME\": ...},\n",
        "        },\n",
        "    },\n",
        "    # present only when return_circuits=True\n",
        "    \"circuits\": [QuantumCircuit, ...],  # one per evolved step; circuits[i] is at times[i + 1]\n",
        "}\n",
        "```\n",
        "\n",
        "`aqc_fidelities` und `circuit_stats` sind die beiden, die man zuerst lesen sollte: Zusammen geben sie Aufschluss darüber, ob die Komprimierung originalgetreu geblieben ist und ob dadurch tatsächlich an Tiefe gewonnen wurde. Am `resource_usage` wird die Wartezeit in der Warteschlange getrennt von der QPU-Zeit ausgewiesen `runtime`, die Ihnen in Rechnung gestellt wird. Eine ungültige Eingabe wird als strukturierter ( `ServerlessError` Code `4615`) schnell zurückgewiesen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-md",
      "metadata": {},
      "source": [
        "<span id=\"simulator-example\" />\n",
        "\n",
        "## Simulator-Beispiel\n",
        "\n",
        "Führen Sie die Funktion zunächst auf genau diesem Backend `statevector` aus. Es beansprucht keine QPU-Zeit und überprüft die Bereitstellung von Anfang bis Ende. Das vorliegende Modell ist eine Ising-Kette mit acht Qubits im transversalen Feld, und `observables` wird weggelassen, sodass die Funktion die standardmäßige „ $Z$ “ pro Knoten misst.\n",
        "\n",
        "Der Komprimierungsplan ist die Angabe, die man verstehen sollte. Jedes Segment `{\"n_steps\": k, \"ansatz_steps\": m}` fasst aufeinanderfolgende `k` Trotter-Schritte zu einem Ansatz zusammen, der aus einem `m`-Schritt-Trotter-Ziel aufgebaut ist, und alle darüber hinausgehenden Schritte werden als reines Trotter-Verfahren `sum(n_steps)` ausgeführt. Frühe Schritte mit geringer Verschränkung lassen sich gut in einen flachen einschichtigen Ansatz komprimieren; spätere Schritte mit stärkerer Verschränkung erfordern einen tieferen Ansatz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "sim-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job ID: ee1f3793-e995-427d-81d1-5924549beb38\n"
          ]
        }
      ],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "fn = serverless.load(\"aqc-dynamics-function\")\n",
        "\n",
        "n = 8\n",
        "H = SparsePauliOp.from_sparse_list(\n",
        "    [(\"ZZ\", [i, i + 1], 1.0) for i in range(n - 1)]\n",
        "    + [(\"X\", [i], 0.8) for i in range(n)],\n",
        "    num_qubits=n,\n",
        ")\n",
        "\n",
        "job = fn.run(\n",
        "    t_steps=8,\n",
        "    aqc_segments=[\n",
        "        {\n",
        "            \"n_steps\": 4,\n",
        "            \"ansatz_steps\": 1,\n",
        "        },  # early steps -> shallow 1-layer ansatz\n",
        "        {\n",
        "            \"n_steps\": 2,\n",
        "            \"ansatz_steps\": 2,\n",
        "        },  # later steps -> deeper 2-layer ansatz\n",
        "    ],\n",
        "    hamiltonian=H,\n",
        "    aqc_options={\"max_bond\": 32},\n",
        "    backend=\"statevector\",\n",
        ")\n",
        "print(\"job ID:\", job.job_id)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "follow-md",
      "metadata": {},
      "source": [
        "<span id=\"follow-the-run-and-read-the-result\" />\n",
        "\n",
        "### Verfolgen Sie den Durchlauf und lesen Sie das Ergebnis ab\n",
        "\n",
        "`status()` Gibt sowohl den groben Lebenszyklus des Auftrags als auch die Teilstatusangaben zu den einzelnen Phasen wieder, die die Funktion während ihrer Ausführung veröffentlicht. Die gleichen Schritte gelten auch für den Hardware-Durchlauf, der später in dieser Anleitung beschrieben wird:\n",
        "\n",
        "`QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE`\n",
        "\n",
        "| `status()` Wert                    | Phase                                                                  |\n",
        "| ---------------------------------- | ---------------------------------------------------------------------- |\n",
        "| `RUNNING: OPTIMIZING_FOR_HARDWARE` | Zustandsvorbereitung, Trotter-Aufbau, AQC-Kompression                  |\n",
        "| `RUNNING: WAITING_FOR_QPU`         | in der Warteschlange der QPU (`runtime` nur Backend)                   |\n",
        "| `RUNNING: EXECUTING_QPU`           | ausgeführte Schaltkreise (lokale Simulatoren kennzeichnen dies direkt) |\n",
        "| `RUNNING: POST_PROCESSING`         | Erstellen des Ergebniswörterbuchs                                      |\n",
        "\n",
        "Die Endzustände sind `DONE`, `ERROR`, und `CANCELED`. Dieser `statevector` Durchlauf hat keine QPU-Warteschlange, daher wird er übersprungen `RUNNING: WAITING_FOR_QPU`. Verwenden Sie `job.logs()` an beliebiger Stelle, um die Protokolle der einzelnen Phasen anzuzeigen, einschließlich der in jedem Schritt erreichten AQC-Genauigkeit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "status-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(job.status())  # re-run until this reports DONE"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 113,
      "id": "result-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']\n",
            "shape: (9, 8) -> (n_times, n_observables)\n",
            "first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]\n",
            "last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]\n",
            "AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}\n",
            "2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "result = job.result()\n",
        "ev = np.array(result[\"expectation_values\"])\n",
        "\n",
        "print(\"observables:\", result[\"observable_labels\"])\n",
        "print(\"shape:\", ev.shape, \"-> (n_times, n_observables)\")\n",
        "print(\"first row (t = 0, the prepared state):\", np.round(ev[0], 4))\n",
        "print(\"last row (t = t_steps * dt):\", np.round(ev[-1], 4))\n",
        "print(\n",
        "    \"AQC fidelities:\",\n",
        "    {k: round(v, 4) for k, v in result[\"metadata\"][\"aqc_fidelities\"].items()},\n",
        ")\n",
        "\n",
        "# What the compression bought: 2-qubit depth at the final time step.\n",
        "stats = result[\"metadata\"][\"circuit_stats\"][\n",
        "    str(result[\"metadata\"][\"t_steps\"])\n",
        "]\n",
        "print(\n",
        "    \"2q depth at the final step:\",\n",
        "    stats[\"full_trotter\"][\"depth_2q\"],\n",
        "    \"(full Trotter) ->\",\n",
        "    stats[\"aqc_trotter\"][\"depth_2q\"],\n",
        "    \"(AQC + Trotter)\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-md",
      "metadata": {},
      "source": [
        "<span id=\"hardware-example\" />\n",
        "\n",
        "## Beispiel für Hardware\n",
        "\n",
        "Ein Funktionsaufruf wird auf einem echten „ IBM Quantum “-Prozessor transpiliert `backend=\"runtime\"` und ausgeführt, wobei die in die Funktion integrierten Fehlerminderungsmechanismen zum Einsatz kommen: dynamische Entkopplung ( XY4 ), Gate-Twirling und Twirled Readout Error Extinction (TREX). `backend_name` wählt das Gerät aus; wird dieser Parameter weggelassen, wählt die Funktion das Gerät mit der geringsten Auslastung aus.\n",
        "\n",
        "Am Wissenschaftscode ändert sich nichts. Im Vergleich zum Simulatorbeispiel unterscheiden sich die Kettenlänge, die Anzahl der Trotter-Schritte, der Komprimierungsplan, das Backend sowie die Einstellungen zur expliziten Fehlerbehebung, die im folgenden Abschnitt behandelt werden.\n",
        "\n",
        "<span id=\"sizing-the-job-for-the-control-hardware\" />\n",
        "\n",
        "### Dimensionierung der Aufgabe für die Steuerungshardware\n",
        "\n",
        "`estimator_options` Lohnt es sich, diese Einstellung bewusst vorzunehmen? Beim „Gate Twirling“ werden für jeden „ PUB “ separate `num_randomizations` , zufällig generierte Schaltkreise erstellt, und der gesamte Vorgang – jeder „ PUB “ mit all seinen Zufallselementen – muss in den Befehlsspeicher des klassischen Steuerungssystems der QPU passen. Die Funktion ist standardmäßig auf 1000 Randomisierungen eingestellt, sodass eine 10-stufige Evolution 11 PUBs mit jeweils 1000 Schaltungen übermittelt: das sind etwa 11.000 Schaltungsinstanzen in einem einzigen Auftrag.\n",
        "\n",
        "Wird der vom Steuerungssystem festgelegte Wert überschritten, schlägt der Vorgang mit [dem Fehler 6073](https://ibm.biz/error_codes#6073) fehl. [Unter „Job-Limits“](/docs/guides/job-limits) werden die Grenzwerte und deren Berechnung erläutert. Der wichtigste Wert ist „ 26.8 “ Millionen Steuerbefehle pro Qubit, der pro Job und nicht pro „ PUB “ gilt. Bei der dynamischen Entkopplung werden Gatter hinzugefügt, die dazu beitragen.\n",
        "\n",
        "Zwei Eingaben bestimmen die Größe:\n",
        "\n",
        "* `estimator_options` legt das Budget für die Aufnahmen fest. Die Gesamtzahl der Schüsse beträgt `num_randomizations * shots_per_randomization`, sodass man die Zufallsgenerierung gegen die Anzahl der Schüsse pro Zufallsgenerierung eintauschen, die Statistiken beibehalten und das Programm dennoch verkleinern kann. Die folgende Zelle verwendet 100 Randomisierungen mit jeweils 200 Schüssen, was 20.000 Schüssen pro Beobachtungsgröße entspricht und etwa einem Zehntel der Schaltungsinstanzen, die die Standardeinstellungen liefern würden. Eine vollständige Übersicht über alle Felder finden Sie unter „ [TwirlingOptions](/docs/api/qiskit-ibm-runtime/options-models-twirling-options) “ und in [den „Estimator“-Optionen](/docs/guides/estimator-options).\n",
        "* `batches` teilt die PUBs auf so viele separate Laufzeit-Jobs auf – genau das ist die Abhilfe, die der Fehler 6073 selbst vorschlägt, und der Grund, warum die Framing-Einstellung pro Job von Bedeutung ist. Mit dieser Einstellung werden pro Auftrag etwa drei PUBs statt elf auf einmal gesendet `batches=4` , und die Aufträge werden gemeinsam in einem Stapel versendet, sodass die Gruppe nur einmal in die Warteschlange gestellt wird, anstatt dass jeder Auftrag einzeln in die Warteschlange gestellt wird.\n",
        "\n",
        "Beachten Sie, dass eine angegebene Einstellung die Standardwerte der Funktion vollständig ersetzt `estimator_options` , anstatt mit diesen zusammengeführt zu werden. Daher werden „Dynamische Entkopplung“ und „TREX“ in der folgenden Zelle erneut festgelegt, um sicherzustellen, dass sie aktiviert bleiben.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "hw-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9\n"
          ]
        }
      ],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "fn = serverless.load(\"aqc-dynamics-function\")\n",
        "\n",
        "n = 10\n",
        "H = SparsePauliOp.from_sparse_list(\n",
        "    [(\"ZZ\", [i, i + 1], 1.0) for i in range(n - 1)]\n",
        "    + [(\"X\", [i], 0.8) for i in range(n)],\n",
        "    num_qubits=n,\n",
        ")\n",
        "\n",
        "job = fn.run(\n",
        "    t_steps=10,\n",
        "    aqc_segments=[\n",
        "        {\n",
        "            \"n_steps\": 3,\n",
        "            \"ansatz_steps\": 1,\n",
        "        },  # early steps -> shallow 1-layer ansatz\n",
        "        {\n",
        "            \"n_steps\": 3,\n",
        "            \"ansatz_steps\": 2,\n",
        "        },  # later steps -> deeper 2-layer ansatz\n",
        "    ],\n",
        "    hamiltonian=H,\n",
        "    aqc_options={\"max_bond\": 32},\n",
        "    backend=\"runtime\",\n",
        "    backend_name=\"ibm_marrakesh\",\n",
        "    # The function defaults to 1000 twirling randomizations, which was too large\n",
        "    # for this device. Total shots is num_randomizations *\n",
        "    # shots_per_randomization, so this is 20,000 shots per observable.\n",
        "    estimator_options={\n",
        "        \"dynamical_decoupling\": {\"enable\": True, \"sequence_type\": \"XY4\"},\n",
        "        \"twirling\": {\n",
        "            \"enable_gates\": True,\n",
        "            \"num_randomizations\": 100,\n",
        "            \"shots_per_randomization\": 200,\n",
        "        },\n",
        "        \"resilience\": {\"measure_mitigation\": True},\n",
        "    },\n",
        ")\n",
        "print(\"job ID (save this to reconnect later):\", job.job_id)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-reconnect-md",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Erneute Verbindung zu einem seit Langem laufenden Job herstellen\">\n",
        "  Ein Hardware-Lauf dauert seine Zeit, und der Großteil der Zeit wird eher klassisch als auf der QPU verbracht. Die AQC-Komprimierung läuft innerhalb der Funktion ab, bevor irgendetwas die QPU erreicht, und die QPU-Warteschlange wird darüberhinaus noch hinzugefügt. Sie müssen dieses Notizbuch oder diesen Kernel während der Ausführung nicht geöffnet lassen.\n",
        "\n",
        "  Kopieren Sie die in der vorherigen Zelle angezeigte Auftrags-ID und speichern Sie sie. In den nächsten drei Zellen können Sie den Lauf später fortsetzen:\n",
        "\n",
        "  1. Erneut verbinden – nur in einer neuen Kernel-Sitzung erforderlich: Führen Sie die [Authentifizierungszelle](#1-authentication) erneut aus, um sie neu zu erstellen `serverless`, und bauen Sie anschließend den `job` Handle anhand der von Ihnen gespeicherten ID wieder auf. Überspringen Sie diese Zelle, wenn Sie sich noch in der Sitzung befinden, in der Sie die Daten übermittelt haben, da das Handle bereits aktiv ist.\n",
        "  2. Status prüfen: So lange wiederholen, bis die Meldung erscheint `DONE`.\n",
        "  3. Ergebnis abrufen: Erst ausführen, wenn der Status ist `DONE`.\n",
        "\n",
        "  Fügen Sie Ihre gespeicherte ID anstelle des Platzhalters in die folgende Zelle „Erneut verbinden“ ein.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "hw-reconnect",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel\n",
        "# session; if you are still in the session where you submitted, the `job` handle\n",
        "# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.\n",
        "job = serverless.get_job_by_id(\"<your job ID>\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 116,
      "id": "hw-status",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n"
          ]
        }
      ],
      "source": [
        "# Re-run this until it reports DONE, then fetch the result in the following cell.\n",
        "print(job.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 118,
      "id": "hw-result-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "backend: runtime\n",
            "shape: (11, 10) -> (n_times, n_observables)\n",
            "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]\n",
            "AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}\n",
            "2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "\n",
        "# Run this only once the preceding status cell reports DONE. result() blocks until\n",
        "# the job finishes, so calling it earlier just waits.\n",
        "result = job.result()\n",
        "ev = np.array(result[\"expectation_values\"])\n",
        "\n",
        "print(\"backend:\", result[\"metadata\"][\"execution_backend\"])\n",
        "print(\"shape:\", ev.shape, \"-> (n_times, n_observables)\")\n",
        "print(\"last row (t = t_steps * dt):\", np.round(ev[-1], 4))\n",
        "print(\n",
        "    \"AQC fidelities:\",\n",
        "    {k: round(v, 4) for k, v in result[\"metadata\"][\"aqc_fidelities\"].items()},\n",
        ")\n",
        "\n",
        "# What the compression bought: 2-qubit depth at the final time step.\n",
        "stats = result[\"metadata\"][\"circuit_stats\"][\n",
        "    str(result[\"metadata\"][\"t_steps\"])\n",
        "]\n",
        "print(\n",
        "    \"2q depth at the final step:\",\n",
        "    stats[\"full_trotter\"][\"depth_2q\"],\n",
        "    \"(full Trotter) ->\",\n",
        "    stats[\"aqc_trotter\"][\"depth_2q\"],\n",
        "    \"(AQC + Trotter)\",\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "nextsteps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Nächste Schritte\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Empfehlungen\">\n",
        "  * Arbeiten Sie [den Workflow „Simulation der Neutronenstreuung mit AQC + Trotter-Dynamik](/docs/tutorials/simulate-neutron-scattering-with-a-serverless-workflow) “ (Serverless) durch – das begleitende Beispiel, das diese bereitgestellte Funktion aufruft, um den dynamischen Strukturfaktor von KCuF$_3$ zu berechnen.\n",
        "  * Lesen Sie die [AQC-Dynamics-Vorlage unter GitHub](https://github.com/qiskit-community/qiskit-function-templates/blob/main/physics/aqc_trotter/), um den vollständigen Eingabe- und Ausgabevertrag, weitere Beispiele sowie Angaben zur Zitierweise zu erhalten.\n",
        "  * Durchsuchen Sie das [Qiskit-Funktionsvorlagen-Repository](https://github.com/qiskit-community/qiskit-function-templates/tree/main/physics/aqc_trotter) nach weiteren Anwendungsvorlagen, die auf dieselbe Weise erstellt wurden.\n",
        "  * Lesen Sie den [Leitfaden „ Qiskit Serverless “ zur Verwaltung bereitgestellter Funktionen.](/docs/guides/serverless)\n",
        "  * Erfahren Sie mehr über die AQC-Komprimierungsstufe mit dem [Qiskit-Add-on: AQC-Tensor](https://qiskit.github.io/qiskit-addon-aqc-tensor/) -Dokumentation.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}