{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "dd701ecc",
      "metadata": {},
      "source": [
        "---\n",
        "title: Approximate quantum compilation for time evolution circuits\n",
        "description: Learn how to use AQC-Tensor to compress Trotterized time-evolution circuits for efficient execution on quantum hardware.\n",
        "---\n",
        "\n",
        "{/* cspell:ignore circo Néel */}\n",
        "\n",
        "# Approximate quantum compilation for time evolution circuits\n",
        "\n",
        "*Usage estimate: 15 seconds on a Heron processor (NOTE: This is an estimate only. Your runtime might vary.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "12608301",
      "metadata": {},
      "source": [
        "## Learning outcomes\n",
        "\n",
        "After completing this tutorial, you can expect to understand the following information:\n",
        "\n",
        "* How to use the AQC-Tensor Qiskit addon to compress deep Trotter circuits into shallow ansatz circuits\n",
        "* How to generate a parametrized ansatz from a Trotter circuit and optimize its parameters using tensor network (MPS) methods\n",
        "* How to evaluate the fidelity of a compressed circuit against the target evolution and run it on quantum hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3c5992a",
      "metadata": {},
      "source": [
        "## Prerequisites\n",
        "\n",
        "It is recommended that you familiarize yourself with these topics:\n",
        "\n",
        "* [Basics of quantum circuits](/learning/courses/basics-of-quantum-information)\n",
        "* [Hamiltonian simulation and Trotterization](/learning/courses/utility-scale-quantum-computing/quantum-simulation)\n",
        "* [Introduction to primitives](/docs/guides/primitives)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "714adc39",
      "metadata": {},
      "source": [
        "## Background\n",
        "\n",
        "This tutorial demonstrates how to implement **Approximate Quantum Compilation** using tensor networks (AQC-Tensor) with Qiskit to enhance quantum circuit performance. AQC-Tensor compresses deep Trotter circuits into shallower, more hardware-friendly circuits while preserving simulation accuracy.\n",
        "\n",
        "### How AQC-Tensor works\n",
        "\n",
        "Consider simulating a Hamiltonian $H$ for total time $t$ using $k$ Trotter steps. The full Trotter circuit is:\n",
        "\n",
        "$$\n",
        "U_{\\text{full}} = \\left[U_{\\text{Trotter}}(t/k)\\right]^k\n",
        "$$\n",
        "\n",
        "A naive approach uses few Trotter steps to keep circuit depth manageable, but this introduces significant Trotter error. AQC-Tensor resolves this tension by separating accuracy from depth:\n",
        "\n",
        "1. **Target circuit (high accuracy, deep):** Construct a Trotter circuit with many steps—say, $10k$—for the same evolution time. This circuit has far less Trotter error, but is too deep for hardware. Because it is only simulated classically as a matrix product state (MPS), depth is not a concern.\n",
        "\n",
        "2. **Ansatz circuit (low depth, parametrized):** Define a parametrized circuit $V(\\theta)$ with the same structure as a single-step Trotter circuit. Initialize it so that $V(\\theta_{\\text{init}}) = U_{\\text{Trotter}}(t/k)$, then iteratively optimize $\\theta$ so that $V(\\theta)$ reproduces the high-accuracy target state as closely as possible.\n",
        "\n",
        "The result is a circuit that retains the depth of a single Trotter step but achieves the accuracy of many, making it feasible for near-term quantum hardware.\n",
        "\n",
        "### When to use AQC-Tensor\n",
        "\n",
        "AQC-Tensor is most effective when:\n",
        "\n",
        "* **Circuit depth exceeds hardware coherence times.** If a Trotter simulation requires more Trotter steps than the device can support, AQC-Tensor can compress the evolution into a shallower circuit.\n",
        "* **Entanglement remains classically tractable.** The total entanglement in a time-evolved state depends primarily on the evolution time $t$, not the number of Trotter steps $k$. This means a target circuit with $10k$ steps is typically no harder to represent as an MPS than one with $k$ steps, as long as $t$ is short enough for bond dimensions to stay manageable.\n",
        "* **A natural ansatz exists.** Because the ansatz mirrors the structure of a Trotter circuit, it provides a physically motivated starting point with well-defined initial parameters, avoiding the convergence issues that can plague arbitrary variational ansatze.\n",
        "\n",
        "This approach contrasts with generic circuit compression: rather than trying to approximate an arbitrary unitary with fewer gates, AQC-Tensor keeps the same gate structure and optimizes its parameters to reduce Trotter error. See the [AQC-Tensor documentation](https://qiskit.github.io/qiskit-addon-aqc-tensor/) for more information.\n",
        "\n",
        "This tutorial guides you through the full state-preparation AQC-Tensor workflow: defining a Hamiltonian, generating Trotter circuits, compressing them via tensor-network optimization, and executing the result on IBM Quantum® hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8a05291",
      "metadata": {},
      "source": [
        "## Requirements\n",
        "\n",
        "Before starting this tutorial, ensure that you have the following installed:\n",
        "\n",
        "* Qiskit SDK v2.0 or later, with [visualization](/docs/en/api/qiskit/visualization) support\n",
        "* Qiskit Runtime v0.22 or later (`pip install qiskit-ibm-runtime`)\n",
        "* AQC-Tensor Qiskit addon (`pip install 'qiskit-addon-aqc-tensor[aer,quimb-jax]'`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f9410d21",
      "metadata": {},
      "source": [
        "## Setup\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "ecdb92e0",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import quimb.tensor\n",
        "import datetime\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from scipy.linalg import expm\n",
        "from scipy.optimize import OptimizeResult, minimize\n",
        "\n",
        "from qiskit.quantum_info import SparsePauliOp, Pauli\n",
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.synthesis import SuzukiTrotter\n",
        "\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    generate_time_evolution_circuit,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.ansatz_generation import (\n",
        "    generate_ansatz_from_circuit,\n",
        ")\n",
        "from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity\n",
        "from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator\n",
        "from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit\n",
        "from qiskit_addon_aqc_tensor.simulation import compute_overlap\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "from qiskit_ibm_runtime.fake_provider import FakeKyiv\n",
        "\n",
        "from rustworkx.visualization import graphviz_draw"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6473f943",
      "metadata": {},
      "source": [
        "## Small-scale simulator example\n",
        "\n",
        "This section uses a 10-site system to illustrate the AQC-Tensor workflow step by step. We simulate the dynamics of a 10-site XXZ spin chain, a widely studied model for examining spin interactions and magnetic properties.\n",
        "\n",
        "The Hamiltonian is as follows:\n",
        "\n",
        "$$\n",
        "\\hat{\\mathcal{H}}_{XXZ} = \\sum_{i=1}^{L-1} J_{i,(i+1)}\\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\\cdot Z_i Z_{(i+1)} \\right) \\, ,\n",
        "$$\n",
        "\n",
        "where $J_{i,(i+1)}$ is a random coefficient for edge $(i, i+1)$ and $L=10$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6520161e",
      "metadata": {},
      "source": [
        "### Step 1: Map classical inputs to a quantum problem\n",
        "\n",
        "In this step, we:\n",
        "\n",
        "1. Define the Hamiltonian, observable, and initial state.\n",
        "2. Compute the exact expectation value classically for later comparison.\n",
        "3. Generate a high-accuracy Trotter circuit (the AQC target) and compress it into a low-depth ansatz using AQC-Tensor.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bbeb5729",
      "metadata": {},
      "source": [
        "#### Set up the Hamiltonian, observable, and initial state\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "527dbada",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hamiltonian: SparsePauliOp(['IIIIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII', 'IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII'],\n",
            "              coeffs=[1.        +0.j, 0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j,\n",
            " 0.60759468+0.j, 0.60759468+0.j, 1.21518937+0.j, 0.55138169+0.j,\n",
            " 0.55138169+0.j, 1.10276338+0.j, 0.52244159+0.j, 0.52244159+0.j,\n",
            " 1.04488318+0.j, 0.4618274 +0.j, 0.4618274 +0.j, 0.9236548 +0.j,\n",
            " 0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j, 0.46879361+0.j,\n",
            " 0.46879361+0.j, 0.93758721+0.j, 0.6958865 +0.j, 0.6958865 +0.j,\n",
            " 1.391773  +0.j, 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j])\n",
            "Observable: SparsePauliOp(['IIIIZZIIII'],\n",
            "              coeffs=[1.+0.j])\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/527dbada-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# L is the number of sites in the 1D spin chain\n",
        "L = 10\n",
        "\n",
        "# Generate the coupling map\n",
        "edge_list = [(i - 1, i) for i in range(1, L)]\n",
        "even_edges = edge_list[::2]\n",
        "odd_edges = edge_list[1::2]\n",
        "coupling_map = CouplingMap(edge_list)\n",
        "\n",
        "# Generate random coefficients for our XXZ Hamiltonian\n",
        "np.random.seed(0)\n",
        "Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)\n",
        "hamiltonian = SparsePauliOp(Pauli(\"I\" * L))\n",
        "for i, edge in enumerate(even_edges + odd_edges):\n",
        "    hamiltonian += SparsePauliOp.from_sparse_list(\n",
        "        [\n",
        "            (\"XX\", (edge), Js[i] / 2),\n",
        "            (\"YY\", (edge), Js[i] / 2),\n",
        "            (\"ZZ\", (edge), Js[i]),\n",
        "        ],\n",
        "        num_qubits=L,\n",
        "    )\n",
        "\n",
        "# Generate a ZZ observable between the two middle qubits\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"ZZ\", (L // 2 - 1, L // 2), 1.0)], num_qubits=L\n",
        ")\n",
        "\n",
        "# Generate an initial Néel state |1010101010⟩\n",
        "initial_state_circuit = QuantumCircuit(L)\n",
        "for i in range(L):\n",
        "    if i % 2:\n",
        "        initial_state_circuit.x(i)\n",
        "\n",
        "print(\"Hamiltonian:\", hamiltonian)\n",
        "print(\"Observable:\", observable)\n",
        "graphviz_draw(coupling_map.graph, method=\"circo\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "54ad6963",
      "metadata": {},
      "source": [
        "#### Compute the exact expectation value\n",
        "\n",
        "For a system of this size, we can compute the exact time-evolved expectation value directly using matrix exponentiation. This serves as our ground truth for evaluating the AQC circuit's accuracy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "20c70651",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AQC evolution time: 0.2\n",
            "Subsequent evolution time: 0.066667\n",
            "Total evolution time: 0.266667\n",
            "Exact expectation value: -0.700899\n"
          ]
        }
      ],
      "source": [
        "aqc_evolution_time = 0.2\n",
        "\n",
        "# Each baseline Trotter step covers dt = aqc_evolution_time / 3\n",
        "# The subsequent (uncompressed) step covers 1 additional dt\n",
        "subsequent_evolution_time = aqc_evolution_time / 3\n",
        "total_evolution_time = aqc_evolution_time + subsequent_evolution_time\n",
        "\n",
        "# Compute exact expectation value via matrix exponentiation\n",
        "H_matrix = hamiltonian.to_matrix()\n",
        "U_exact = expm(-1j * H_matrix * total_evolution_time)\n",
        "\n",
        "# Build the initial state vector (Néel state)\n",
        "initial_state_vec = np.zeros(2**L)\n",
        "state_idx = sum(2**i for i in range(L) if i % 2)\n",
        "initial_state_vec[state_idx] = 1.0\n",
        "\n",
        "# Evolve and compute expectation value\n",
        "evolved_state = U_exact @ initial_state_vec\n",
        "obs_matrix = observable.to_matrix()\n",
        "exact_expval = (evolved_state.conj() @ obs_matrix @ evolved_state).real\n",
        "\n",
        "print(f\"AQC evolution time: {aqc_evolution_time}\")\n",
        "print(f\"Subsequent evolution time: {subsequent_evolution_time:.6f}\")\n",
        "print(f\"Total evolution time: {total_evolution_time:.6f}\")\n",
        "print(f\"Exact expectation value: {exact_expval:.6f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a93c047c",
      "metadata": {},
      "source": [
        "#### Generate the AQC target circuit\n",
        "\n",
        "We now construct the Trotter circuit that will serve as the AQC target. This circuit uses many Trotter steps (32) for high accuracy. Because it will only be simulated classically as an MPS—not executed on hardware—the large depth is not a concern.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "db66bf00",
      "metadata": {},
      "outputs": [],
      "source": [
        "aqc_target_num_trotter_steps = 32\n",
        "\n",
        "aqc_target_circuit = initial_state_circuit.copy()\n",
        "aqc_target_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),\n",
        "        time=aqc_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bce1ef52",
      "metadata": {},
      "source": [
        "#### Generate an ansatz, initial parameters, subsequent circuit, and a baseline circuit\n",
        "\n",
        "Next, we construct a \"good\" circuit with the same evolution time as the AQC target but far fewer Trotter steps (only one). We pass this circuit to `generate_ansatz_from_circuit`, which returns:\n",
        "\n",
        "1. A general, parametrized **ansatz** circuit with the same two-qubit connectivity.\n",
        "2. **Initial parameters** that reproduce the input circuit when plugged into the ansatz.\n",
        "\n",
        "We also construct:\n",
        "\n",
        "* A **subsequent circuit** with one Trotter step that will be appended (uncompressed) after the AQC-optimized portion, following the approach in the [AQC-Tensor initial state tutorial](https://qiskit.github.io/qiskit-addon-aqc-tensor/tutorials/01_initial_state_aqc.html).\n",
        "* A **baseline Trotter circuit** using four Trotter steps over the full evolution time (`aqc_evolution_time + subsequent_evolution_time`). This serves as the comparison: it represents what you would run on hardware without AQC. The AQC ansatz (3 compressed steps + 1 uncompressed step) achieves better accuracy at lower depth.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "78f2665e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Target circuit:      depth 384\n",
            "Baseline circuit:    depth 48 (4 Trotter steps, time=0.2667)\n",
            "Subsequent circuit:  depth 12 (1 Trotter step, time=0.0667)\n",
            "Ansatz circuit:      depth 3, with 156 parameters\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/78f2665e-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "aqc_ansatz_num_trotter_steps = 1\n",
        "\n",
        "aqc_good_circuit = initial_state_circuit.copy()\n",
        "aqc_good_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),\n",
        "        time=aqc_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "\n",
        "aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(\n",
        "    aqc_good_circuit\n",
        ")\n",
        "\n",
        "# Subsequent circuit: 1 non-compressed Trotter step appended after AQC\n",
        "subsequent_num_trotter_steps = 1\n",
        "subsequent_circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian,\n",
        "    synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),\n",
        "    time=subsequent_evolution_time,\n",
        ")\n",
        "\n",
        "# Baseline Trotter circuit: 4 Trotter steps over total evolution time, no AQC\n",
        "baseline_num_trotter_steps = 4\n",
        "baseline_circuit = initial_state_circuit.copy()\n",
        "baseline_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=baseline_num_trotter_steps),\n",
        "        time=total_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "\n",
        "print(\n",
        "    f\"Target circuit:      depth {aqc_target_circuit.depth(lambda x: x.operation.num_qubits == 2)}\"\n",
        ")\n",
        "print(\n",
        "    f\"Baseline circuit:    depth {baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({baseline_num_trotter_steps} Trotter steps, time={total_evolution_time:.4f})\"\n",
        ")\n",
        "print(\n",
        "    f\"Subsequent circuit:  depth {subsequent_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({subsequent_num_trotter_steps} Trotter step, time={subsequent_evolution_time:.4f})\"\n",
        ")\n",
        "print(\n",
        "    f\"Ansatz circuit:      depth {aqc_ansatz.depth(lambda x: x.operation.num_qubits == 2)}, with {len(aqc_initial_parameters)} parameters\"\n",
        ")\n",
        "aqc_ansatz.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be1b4049",
      "metadata": {},
      "source": [
        "#### Set up tensor network simulation and build the target MPS\n",
        "\n",
        "We use the [quimb](https://github.com/jcmgray/quimb) matrix-product state (MPS) circuit simulator, with JAX providing automatic differentiation for the gradient-based optimization. We then build an MPS representation of the target state and evaluate the starting fidelity between the initial ansatz and the target. As the problem instance is a relatively small example, the starting fidelity starts off quite high.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "666fcf42",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Target MPS maximum bond dimension: 5\n",
            "Starting fidelity: 0.998246\n"
          ]
        }
      ],
      "source": [
        "simulator_settings = QuimbSimulator(\n",
        "    quimb.tensor.CircuitMPS, autodiff_backend=\"jax\"\n",
        ")\n",
        "\n",
        "aqc_target_mps = tensornetwork_from_circuit(\n",
        "    aqc_target_circuit, simulator_settings\n",
        ")\n",
        "print(\"Target MPS maximum bond dimension:\", aqc_target_mps.psi.max_bond())\n",
        "\n",
        "good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)\n",
        "starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2\n",
        "print(f\"Starting fidelity: {starting_fidelity:.6f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f14eb3f",
      "metadata": {},
      "source": [
        "#### Optimize the ansatz parameters\n",
        "\n",
        "We minimize the `MaximizeStateFidelity` cost function using the L-BFGS-B optimizer. The optimizer iteratively adjusts the ansatz parameters to maximize the fidelity between the ansatz circuit and the target MPS.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "6ad144d6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "2026-05-18 13:14:49.731596 Intermediate result: Fidelity 0.99952882\n",
            "2026-05-18 13:14:49.734425 Intermediate result: Fidelity 0.99958531\n",
            "2026-05-18 13:14:49.737101 Intermediate result: Fidelity 0.99960093\n",
            "2026-05-18 13:14:49.739813 Intermediate result: Fidelity 0.99961046\n",
            "2026-05-18 13:14:49.742969 Intermediate result: Fidelity 0.99962560\n",
            "2026-05-18 13:14:49.745916 Intermediate result: Fidelity 0.99964395\n",
            "2026-05-18 13:14:49.748615 Intermediate result: Fidelity 0.99968150\n",
            "2026-05-18 13:14:49.753684 Intermediate result: Fidelity 0.99970569\n",
            "2026-05-18 13:14:49.756208 Intermediate result: Fidelity 0.99973788\n",
            "2026-05-18 13:14:49.759067 Intermediate result: Fidelity 0.99975385\n",
            "2026-05-18 13:14:49.762321 Intermediate result: Fidelity 0.99976458\n",
            "2026-05-18 13:14:49.765526 Intermediate result: Fidelity 0.99977661\n",
            "2026-05-18 13:14:49.768496 Intermediate result: Fidelity 0.99978663\n",
            "2026-05-18 13:14:49.771278 Intermediate result: Fidelity 0.99980236\n",
            "2026-05-18 13:14:49.773735 Intermediate result: Fidelity 0.99981607\n",
            "2026-05-18 13:14:49.776339 Intermediate result: Fidelity 0.99982811\n",
            "2026-05-18 13:14:49.779177 Intermediate result: Fidelity 0.99985827\n",
            "2026-05-18 13:14:49.782243 Intermediate result: Fidelity 0.99988354\n",
            "2026-05-18 13:14:49.784904 Intermediate result: Fidelity 0.99991608\n",
            "2026-05-18 13:14:49.787737 Intermediate result: Fidelity 0.99993336\n",
            "2026-05-18 13:14:49.790414 Intermediate result: Fidelity 0.99993956\n",
            "2026-05-18 13:14:49.793029 Intermediate result: Fidelity 0.99994421\n",
            "2026-05-18 13:14:49.795585 Intermediate result: Fidelity 0.99994743\n",
            "2026-05-18 13:14:49.835045 Intermediate result: Fidelity 0.99994791\n",
            "2026-05-18 13:14:49.839786 Intermediate result: Fidelity 0.99994803\n",
            "2026-05-18 13:14:49.842403 Intermediate result: Fidelity 0.99994898\n",
            "2026-05-18 13:14:49.873779 Intermediate result: Fidelity 0.99994898\n",
            "Done after 27 iterations.\n"
          ]
        }
      ],
      "source": [
        "aqc_stopping_fidelity = 1\n",
        "aqc_max_iterations = 500\n",
        "\n",
        "stopping_point = 1.0 - aqc_stopping_fidelity\n",
        "objective = MaximizeStateFidelity(\n",
        "    aqc_target_mps, aqc_ansatz, simulator_settings\n",
        ")\n",
        "\n",
        "\n",
        "def callback(intermediate_result: OptimizeResult):\n",
        "    fidelity = 1 - intermediate_result.fun\n",
        "    print(\n",
        "        f\"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}\"\n",
        "    )\n",
        "    if intermediate_result.fun < stopping_point:\n",
        "        raise StopIteration\n",
        "\n",
        "\n",
        "result = minimize(\n",
        "    objective,\n",
        "    aqc_initial_parameters,\n",
        "    method=\"L-BFGS-B\",\n",
        "    jac=True,\n",
        "    options={\"maxiter\": aqc_max_iterations},\n",
        "    callback=callback,\n",
        ")\n",
        "if result.status not in (0, 1, 99):\n",
        "    raise RuntimeError(\n",
        "        f\"Optimization failed: {result.message} (status={result.status})\"\n",
        "    )\n",
        "\n",
        "print(f\"Done after {result.nit} iterations.\")\n",
        "aqc_final_parameters = result.x"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ea5b484a",
      "metadata": {},
      "source": [
        "#### Assemble the final AQC circuit\n",
        "\n",
        "With the optimized parameters in hand, we bind them to the ansatz and then append the subsequent (uncompressed) Trotter step. The resulting circuit has the depth of a single compressed Trotter step plus one uncompressed step, but the compressed portion approximates the accuracy of 32 Trotter steps.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "e09e40de",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/e09e40de-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)\n",
        "aqc_final_circuit.compose(subsequent_circuit, inplace=True)\n",
        "aqc_final_circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "047511db",
      "metadata": {},
      "source": [
        "### Step 2: Optimize problem for quantum hardware execution\n",
        "\n",
        "For this small-scale example, we use a fake backend (`FakeKyiv`) to simulate hardware execution locally. We transpile both the AQC-optimized circuit (`aqc_final_circuit`) and the baseline Trotter circuit (`baseline_circuit`, four Trotter steps over the full evolution time, no AQC) to the backend's instruction set architecture (ISA), with `optimization_level=3` to further reduce circuit depth.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "9e7556dd",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "AQC circuit depth: 15\n",
            "Baseline Trotter circuit depth: 27\n"
          ]
        }
      ],
      "source": [
        "backend = FakeKyiv()\n",
        "\n",
        "pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend, optimization_level=3\n",
        ")\n",
        "\n",
        "# Transpile the AQC-optimized circuit (compressed + subsequent step)\n",
        "isa_circuit = pass_manager.run(aqc_final_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "print(\n",
        "    \"AQC circuit depth:\",\n",
        "    isa_circuit.depth(lambda x: x.operation.num_qubits == 2),\n",
        ")\n",
        "\n",
        "# Transpile the baseline Trotter circuit (no AQC optimization)\n",
        "isa_baseline_circuit = pass_manager.run(baseline_circuit)\n",
        "isa_baseline_observable = observable.apply_layout(isa_baseline_circuit.layout)\n",
        "print(\n",
        "    \"Baseline Trotter circuit depth:\",\n",
        "    isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "60aec566",
      "metadata": {},
      "source": [
        "### Step 3: Execute using Qiskit primitives\n",
        "\n",
        "We use the [`EstimatorV2`](/docs/api/qiskit-ibm-runtime/estimator-v2) primitive with the fake backend to run both the AQC-optimized circuit and the baseline Trotter circuit, measuring the ZZ observable for each.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "241e24f2",
      "metadata": {},
      "outputs": [],
      "source": [
        "estimator = Estimator(backend)\n",
        "\n",
        "# Run both circuits\n",
        "aqc_result = estimator.run([(isa_circuit, isa_observable)]).result()\n",
        "baseline_result = estimator.run(\n",
        "    [(isa_baseline_circuit, isa_baseline_observable)]\n",
        ").result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0b980055",
      "metadata": {},
      "source": [
        "### Step 4: Post-process and return result in desired classical format\n",
        "\n",
        "We extract the expectation values from both runs and compare them to the exact result. The baseline Trotter circuit shows what we would get without AQC at the same circuit depth, while the AQC circuit demonstrates the improvement from tensor-network optimization.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "af07a1d9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Exact:              -0.7009\n",
            "Baseline Trotter:   -0.5400, |Δ| = 0.1609  (depth 27, 4 steps)\n",
            "AQC (3+1):          -0.5728, |Δ| = 0.1281  (depth 15, compressed+subsequent)\n"
          ]
        }
      ],
      "source": [
        "aqc_expval = aqc_result[0].data.evs.tolist()\n",
        "baseline_expval = baseline_result[0].data.evs.tolist()\n",
        "\n",
        "print(f\"Exact:              {exact_expval:.4f}\")\n",
        "print(\n",
        "    f\"Baseline Trotter:   {baseline_expval:.4f}, |\\u0394| = {np.abs(exact_expval - baseline_expval):.4f}  (depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)}, {baseline_num_trotter_steps} steps)\"\n",
        ")\n",
        "print(\n",
        "    f\"AQC (3+1):          {aqc_expval:.4f}, |\\u0394| = {np.abs(exact_expval - aqc_expval):.4f}  (depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)}, compressed+subsequent)\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "77c39ba8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/77c39ba8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.style.use(\"seaborn-v0_8\")\n",
        "\n",
        "labels = [\n",
        "    f\"Baseline Trotter\\n({baseline_num_trotter_steps} steps, depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)})\",\n",
        "    f\"AQC (3+1)\\n(depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)})\",\n",
        "]\n",
        "values = [baseline_expval, aqc_expval]\n",
        "colors = [\"tab:orange\", \"tab:blue\"]\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "bars = plt.bar(labels, values, color=colors, width=0.5)\n",
        "plt.axhline(\n",
        "    y=exact_expval,\n",
        "    color=\"tab:green\",\n",
        "    linestyle=\"--\",\n",
        "    linewidth=2,\n",
        "    label=f\"Exact ({exact_expval:.4f})\",\n",
        ")\n",
        "plt.ylabel(\"Expected Value\")\n",
        "plt.title(\n",
        "    \"AQC-Tensor (3 compressed + 1 uncompressed) vs Baseline Trotter (10-site XXZ)\"\n",
        ")\n",
        "plt.legend()\n",
        "for bar in bars:\n",
        "    y_val = bar.get_height()\n",
        "    plt.text(\n",
        "        bar.get_x() + bar.get_width() / 2.0,\n",
        "        y_val,\n",
        "        f\"{y_val:.4f}\",\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\" if y_val >= 0 else \"top\",\n",
        "    )\n",
        "plt.axhline(y=0, color=\"black\", linewidth=0.3)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "37062efa",
      "metadata": {},
      "source": [
        "## Large-scale hardware example\n",
        "\n",
        "We now scale up to a 50-site XXZ model to demonstrate AQC-Tensor on a more realistic problem size. The workflow is the same as the small-scale example: we compress three Trotter steps via AQC and append one uncompressed step.\n",
        "\n",
        "For a system of this size, matrix exponentiation is infeasible ($2^{50}$ dimensions), so we compute the reference expectation value directly from a high-accuracy MPS evolved for the full time.\n",
        "\n",
        "### Steps 1–4 combined\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "b6c0f26a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Target circuit:  depth 385\n",
            "Ansatz circuit:  depth 7, with 816 parameters\n",
            "Subsequent circuit: depth 12\n",
            "Baseline circuit:   depth 49 (4 steps, time=0.2667)\n",
            "Target MPS maximum bond dimension: 5\n",
            "Reference expectation value (from MPS): -0.738669\n",
            "2026-05-18 13:02:11.219150 Intermediate result: Fidelity 0.99795732\n",
            "2026-05-18 13:02:11.232256 Intermediate result: Fidelity 0.99822481\n",
            "2026-05-18 13:02:11.245160 Intermediate result: Fidelity 0.99829520\n",
            "2026-05-18 13:02:11.257765 Intermediate result: Fidelity 0.99832379\n",
            "2026-05-18 13:02:11.270280 Intermediate result: Fidelity 0.99836416\n",
            "2026-05-18 13:02:11.284116 Intermediate result: Fidelity 0.99840073\n",
            "2026-05-18 13:02:11.296856 Intermediate result: Fidelity 0.99846863\n",
            "2026-05-18 13:02:11.309602 Intermediate result: Fidelity 0.99865244\n",
            "2026-05-18 13:02:11.322012 Intermediate result: Fidelity 0.99872665\n",
            "2026-05-18 13:02:11.334195 Intermediate result: Fidelity 0.99892335\n",
            "2026-05-18 13:02:11.346570 Intermediate result: Fidelity 0.99901045\n",
            "2026-05-18 13:02:11.359202 Intermediate result: Fidelity 0.99907181\n",
            "2026-05-18 13:02:11.371511 Intermediate result: Fidelity 0.99911125\n",
            "2026-05-18 13:02:11.383870 Intermediate result: Fidelity 0.99918585\n",
            "2026-05-18 13:02:11.396184 Intermediate result: Fidelity 0.99921504\n",
            "2026-05-18 13:02:11.408543 Intermediate result: Fidelity 0.99924936\n",
            "2026-05-18 13:02:11.422557 Intermediate result: Fidelity 0.99929226\n",
            "2026-05-18 13:02:11.436275 Intermediate result: Fidelity 0.99933099\n",
            "2026-05-18 13:02:11.449511 Intermediate result: Fidelity 0.99935792\n",
            "2026-05-18 13:02:11.462093 Intermediate result: Fidelity 0.99937925\n",
            "2026-05-18 13:02:11.475783 Intermediate result: Fidelity 0.99940690\n",
            "2026-05-18 13:02:11.490254 Intermediate result: Fidelity 0.99944409\n",
            "2026-05-18 13:02:11.503292 Intermediate result: Fidelity 0.99946840\n",
            "2026-05-18 13:02:11.516064 Intermediate result: Fidelity 0.99949378\n",
            "2026-05-18 13:02:11.532861 Intermediate result: Fidelity 0.99951380\n",
            "2026-05-18 13:02:11.546182 Intermediate result: Fidelity 0.99955313\n",
            "2026-05-18 13:02:11.559168 Intermediate result: Fidelity 0.99955707\n",
            "2026-05-18 13:02:11.571753 Intermediate result: Fidelity 0.99959306\n",
            "2026-05-18 13:02:11.584257 Intermediate result: Fidelity 0.99960486\n",
            "2026-05-18 13:02:11.597610 Intermediate result: Fidelity 0.99961714\n",
            "2026-05-18 13:02:11.610106 Intermediate result: Fidelity 0.99962953\n",
            "2026-05-18 13:02:11.622515 Intermediate result: Fidelity 0.99963525\n",
            "2026-05-18 13:02:11.635543 Intermediate result: Fidelity 0.99964658\n",
            "2026-05-18 13:02:11.649044 Intermediate result: Fidelity 0.99965027\n",
            "2026-05-18 13:02:11.664148 Intermediate result: Fidelity 0.99965802\n",
            "2026-05-18 13:02:11.678033 Intermediate result: Fidelity 0.99966731\n",
            "2026-05-18 13:02:11.692714 Intermediate result: Fidelity 0.99967780\n",
            "2026-05-18 13:02:11.706753 Intermediate result: Fidelity 0.99968567\n",
            "2026-05-18 13:02:11.720780 Intermediate result: Fidelity 0.99969139\n",
            "2026-05-18 13:02:11.733471 Intermediate result: Fidelity 0.99969628\n",
            "2026-05-18 13:02:11.745998 Intermediate result: Fidelity 0.99970331\n",
            "2026-05-18 13:02:11.758424 Intermediate result: Fidelity 0.99970796\n",
            "2026-05-18 13:02:11.771986 Intermediate result: Fidelity 0.99971165\n",
            "2026-05-18 13:02:11.785841 Intermediate result: Fidelity 0.99971892\n",
            "2026-05-18 13:02:11.799105 Intermediate result: Fidelity 0.99972226\n",
            "2026-05-18 13:02:11.811623 Intermediate result: Fidelity 0.99972441\n",
            "2026-05-18 13:02:11.824114 Intermediate result: Fidelity 0.99972679\n",
            "2026-05-18 13:02:11.837179 Intermediate result: Fidelity 0.99972965\n",
            "2026-05-18 13:02:12.345479 Intermediate result: Fidelity 0.99972965\n",
            "Done after 49 iterations.\n",
            "<IBMBackend('ibm_pittsburgh')>\n",
            "AQC circuit depth: 71\n",
            "Baseline Trotter circuit depth: 111\n",
            "Job ID: d85kc6o0bvlc73d5nhn0\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "\n",
        "# Define the 50-site spin chain\n",
        "L = 50\n",
        "edge_list = [(i - 1, i) for i in range(1, L)]\n",
        "even_edges = edge_list[::2]\n",
        "odd_edges = edge_list[1::2]\n",
        "coupling_map = CouplingMap(edge_list)\n",
        "\n",
        "# Random XXZ Hamiltonian\n",
        "np.random.seed(0)\n",
        "Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)\n",
        "hamiltonian = SparsePauliOp(Pauli(\"I\" * L))\n",
        "for i, edge in enumerate(even_edges + odd_edges):\n",
        "    hamiltonian += SparsePauliOp.from_sparse_list(\n",
        "        [\n",
        "            (\"XX\", (edge), Js[i] / 2),\n",
        "            (\"YY\", (edge), Js[i] / 2),\n",
        "            (\"ZZ\", (edge), Js[i]),\n",
        "        ],\n",
        "        num_qubits=L,\n",
        "    )\n",
        "\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"ZZ\", (L // 2 - 1, L // 2), 1.0)], num_qubits=L\n",
        ")\n",
        "\n",
        "# Initial Néel state\n",
        "initial_state_circuit = QuantumCircuit(L)\n",
        "for i in range(L):\n",
        "    if i % 2:\n",
        "        initial_state_circuit.x(i)\n",
        "\n",
        "# Time parameters\n",
        "aqc_evolution_time = 0.2\n",
        "subsequent_evolution_time = aqc_evolution_time / 3\n",
        "total_evolution_time = aqc_evolution_time + subsequent_evolution_time\n",
        "\n",
        "# AQC target circuit (high-accuracy, 32 Trotter steps for AQC portion)\n",
        "aqc_target_num_trotter_steps = 32\n",
        "\n",
        "aqc_target_circuit = initial_state_circuit.copy()\n",
        "aqc_target_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),\n",
        "        time=aqc_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "\n",
        "# Generate ansatz from 1-step Trotter circuit\n",
        "aqc_good_circuit = initial_state_circuit.copy()\n",
        "aqc_good_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=1),\n",
        "        time=aqc_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "\n",
        "aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(\n",
        "    aqc_good_circuit\n",
        ")\n",
        "\n",
        "# Subsequent circuit: 1 non-compressed Trotter step\n",
        "subsequent_circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian,\n",
        "    synthesis=SuzukiTrotter(reps=1),\n",
        "    time=subsequent_evolution_time,\n",
        ")\n",
        "\n",
        "# Baseline Trotter circuit: 4 Trotter steps over total evolution time, no AQC\n",
        "baseline_num_trotter_steps = 4\n",
        "baseline_circuit = initial_state_circuit.copy()\n",
        "baseline_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=baseline_num_trotter_steps),\n",
        "        time=total_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "print(\n",
        "    f\"Target circuit:  depth {aqc_target_circuit.depth(lambda x: x.operation.num_qubits == 2)}\"\n",
        ")\n",
        "print(\n",
        "    f\"Ansatz circuit:  depth {aqc_ansatz.depth(lambda x: x.operation.num_qubits == 2)}, with {len(aqc_initial_parameters)} parameters\"\n",
        ")\n",
        "print(\n",
        "    f\"Subsequent circuit: depth {subsequent_circuit.depth(lambda x: x.operation.num_qubits == 2)}\"\n",
        ")\n",
        "print(\n",
        "    f\"Baseline circuit:   depth {baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)} ({baseline_num_trotter_steps} steps, time={total_evolution_time:.4f})\"\n",
        ")\n",
        "\n",
        "# Build target MPS and compute reference expectation value\n",
        "simulator_settings = QuimbSimulator(\n",
        "    quimb.tensor.CircuitMPS, autodiff_backend=\"jax\"\n",
        ")\n",
        "aqc_target_mps = tensornetwork_from_circuit(\n",
        "    aqc_target_circuit, simulator_settings\n",
        ")\n",
        "print(\"Target MPS maximum bond dimension:\", aqc_target_mps.psi.max_bond())\n",
        "\n",
        "# For the reference expectation value, we need the full evolution (AQC + subsequent)\n",
        "# Build a high-accuracy full circuit for MPS reference\n",
        "full_target_circuit = initial_state_circuit.copy()\n",
        "full_target_circuit.compose(\n",
        "    generate_time_evolution_circuit(\n",
        "        hamiltonian,\n",
        "        synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),\n",
        "        time=total_evolution_time,\n",
        "    ),\n",
        "    inplace=True,\n",
        ")\n",
        "full_target_mps = tensornetwork_from_circuit(\n",
        "    full_target_circuit, simulator_settings\n",
        ")\n",
        "exact_expval = full_target_mps.local_expectation(\n",
        "    quimb.pauli(\"Z\") & quimb.pauli(\"Z\"), (L // 2 - 1, L // 2)\n",
        ").real.item()\n",
        "print(f\"Reference expectation value (from MPS): {exact_expval:.6f}\")\n",
        "\n",
        "# Optimize ansatz parameters\n",
        "objective = MaximizeStateFidelity(\n",
        "    aqc_target_mps, aqc_ansatz, simulator_settings\n",
        ")\n",
        "\n",
        "\n",
        "def callback(intermediate_result: OptimizeResult):\n",
        "    fidelity = 1 - intermediate_result.fun\n",
        "    print(\n",
        "        f\"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}\"\n",
        "    )\n",
        "\n",
        "\n",
        "result = minimize(\n",
        "    objective,\n",
        "    aqc_initial_parameters,\n",
        "    method=\"L-BFGS-B\",\n",
        "    jac=True,\n",
        "    options={\"maxiter\": 500},\n",
        "    callback=callback,\n",
        ")\n",
        "if result.status not in (0, 1, 99):\n",
        "    raise RuntimeError(\n",
        "        f\"Optimization failed: {result.message} (status={result.status})\"\n",
        "    )\n",
        "print(f\"Done after {result.nit} iterations.\")\n",
        "\n",
        "# Assemble the final AQC circuit: optimized ansatz + subsequent Trotter step\n",
        "aqc_final_circuit = aqc_ansatz.assign_parameters(result.x)\n",
        "aqc_final_circuit.compose(subsequent_circuit, inplace=True)\n",
        "\n",
        "# -------------------------Step 2-------------------------\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(min_num_qubits=127)\n",
        "print(backend)\n",
        "\n",
        "pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend, optimization_level=3\n",
        ")\n",
        "isa_circuit = pass_manager.run(aqc_final_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "print(\n",
        "    \"AQC circuit depth:\",\n",
        "    isa_circuit.depth(lambda x: x.operation.num_qubits == 2),\n",
        ")\n",
        "\n",
        "# Also transpile the baseline Trotter circuit (4 Trotter steps, no AQC)\n",
        "isa_baseline_circuit = pass_manager.run(baseline_circuit)\n",
        "isa_baseline_observable = observable.apply_layout(isa_baseline_circuit.layout)\n",
        "print(\n",
        "    \"Baseline Trotter circuit depth:\",\n",
        "    isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2),\n",
        ")\n",
        "\n",
        "# -------------------------Step 3-------------------------\n",
        "\n",
        "# Submit both circuits in a single job\n",
        "estimator = Estimator(backend)\n",
        "estimator.options.environment.job_tags = [\"TUT_AQCTE\"]\n",
        "\n",
        "job = estimator.run(\n",
        "    [\n",
        "        (isa_circuit, isa_observable),\n",
        "        (isa_baseline_circuit, isa_baseline_observable),\n",
        "    ]\n",
        ")\n",
        "print(\"Job ID:\", job.job_id())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "a4dc23fd-494e-46cb-a8f5-d1cd444b96f4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Exact (MPS):        -0.7387\n",
            "Baseline Trotter:   -0.5955, |Δ| = 0.1432\n",
            "AQC (3+1):          -0.6734, |Δ| = 0.0653\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/a4dc23fd-494e-46cb-a8f5-d1cd444b96f4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "\n",
        "hw_results = job.result()\n",
        "aqc_expval = hw_results[0].data.evs.tolist()\n",
        "baseline_expval = hw_results[1].data.evs.tolist()\n",
        "\n",
        "print(f\"Exact (MPS):        {exact_expval:.4f}\")\n",
        "print(\n",
        "    f\"Baseline Trotter:   {baseline_expval:.4f}, |\\u0394| = {np.abs(exact_expval - baseline_expval):.4f}\"\n",
        ")\n",
        "print(\n",
        "    f\"AQC (3+1):          {aqc_expval:.4f}, |\\u0394| = {np.abs(exact_expval - aqc_expval):.4f}\"\n",
        ")\n",
        "\n",
        "labels = [\n",
        "    f\"Baseline Trotter\\n({baseline_num_trotter_steps} steps, depth {isa_baseline_circuit.depth(lambda x: x.operation.num_qubits == 2)})\",\n",
        "    f\"AQC (3+1)\\n(depth {isa_circuit.depth(lambda x: x.operation.num_qubits == 2)})\",\n",
        "]\n",
        "values = [baseline_expval, aqc_expval]\n",
        "colors = [\"tab:orange\", \"tab:blue\"]\n",
        "\n",
        "plt.figure(figsize=(8, 5))\n",
        "bars = plt.bar(labels, values, color=colors, width=0.5)\n",
        "plt.axhline(\n",
        "    y=exact_expval,\n",
        "    color=\"tab:green\",\n",
        "    linestyle=\"--\",\n",
        "    linewidth=2,\n",
        "    label=f\"Exact ({exact_expval:.4f})\",\n",
        ")\n",
        "plt.ylabel(\"Expected Value\")\n",
        "plt.title(\n",
        "    \"AQC-Tensor (3 compressed + 1 uncompressed) vs Baseline Trotter (50-site XXZ)\"\n",
        ")\n",
        "plt.legend()\n",
        "for bar in bars:\n",
        "    y_val = bar.get_height()\n",
        "    plt.text(\n",
        "        bar.get_x() + bar.get_width() / 2.0,\n",
        "        y_val,\n",
        "        f\"{y_val:.4f}\",\n",
        "        ha=\"center\",\n",
        "        va=\"bottom\" if y_val >= 0 else \"top\",\n",
        "    )\n",
        "plt.axhline(y=0, color=\"black\", linewidth=0.3)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "278bc002",
      "metadata": {},
      "source": [
        "## Next steps\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommendations\">\n",
        "  If you found this work interesting, you might be interested in the following material:\n",
        "\n",
        "  * [AQC-Tensor addon documentation](https://qiskit.github.io/qiskit-addon-aqc-tensor/) — includes the related **unitary AQC** technique, which optimizes parametrized circuits to approximate a target unitary operator rather than a prepared state\n",
        "  * [Error mitigation and suppression techniques](/docs/guides/error-mitigation-and-suppression-techniques)\n",
        "  * [Combine error mitigation techniques](/docs/tutorials/combine-error-mitigation-techniques)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "description": "Learn how to use AQC-Tensor to compress Trotterized time-evolution circuits for efficient execution on quantum hardware.",
    "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"
    },
    "title": "Approximate quantum compilation for time evolution circuits"
  },
  "nbformat": 4,
  "nbformat_minor": 5
}