qiskit-documentation/docs/tutorials/approximate-quantum-compila...

1890 lines
77 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"id": "2c991cef-110a-455a-8741-52d5e20d3196",
"metadata": {},
"source": [
"# Approximate quantum compilation for time evolution circuits\n",
"*Usage estimate: 5 minutes on IBM Brisbane (NOTE: This is an estimate only. Your runtime may vary.)*"
]
},
{
"cell_type": "markdown",
"id": "93895788-7d38-4cc7-a7e9-ba97c34966af",
"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. We apply AQC-Tensor within the context of a Trotterized time evolution to reduce circuit depth while maintaining simulation accuracy, following the Qiskit framework for state preparation and optimization. In this notebook, you'll learn how to create a low-depth ansatz circuit from an initial Trotter circuit, optimize it with tensor networks, and prepare it for quantum hardware execution.\n",
"\n",
"The primary objective is to simulate time evolution for a model Hamiltonian with a reduced circuit depth. This is achieved using the **AQC-Tensor** Qiskit addon, [qiskit-addon-aqc-tensor](https://github.com/Qiskit/qiskit-addon-aqc-tensor), which leverages tensor networks, specifically matrix product states (MPS), to compress and optimize the initial circuit. Through iterative adjustments, the compressed ansatz circuit maintains fidelity to the original circuit while staying feasible for near-term quantum hardware. More details can be found in the corresponding [docs](/docs/guides/qiskit-addons-aqc) with a [simple example](/docs/guides/qiskit-addons-aqc-get-started) to get started.\n",
"\n",
"Approximate Quantum Compilation is particularly advantageous in quantum simulations that exceed hardware coherence times, as it allows complex simulations to be performed more efficiently. This tutorial will guide you through the AQC-Tensor workflow setup in Qiskit, covering initialization of a Hamiltonian, generation of Trotter circuits, and transpilation of the final optimized circuit for a target device."
]
},
{
"cell_type": "markdown",
"id": "b1b8f238-0dab-42c5-b658-779f3ff178a0",
"metadata": {},
"source": [
"## Requirements\n",
"\n",
"Before starting this tutorial, ensure that you have the following installed:\n",
"\n",
"* Qiskit SDK v1.0 or later, with visualization support (`pip install 'qiskit[visualization]'`)\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",
"* rustworkx v0.15 or later (`pip install rustworkx`)"
]
},
{
"cell_type": "markdown",
"id": "f0723f2d-e0ba-4fe9-a742-1891b7b45459",
"metadata": {},
"source": [
"## Setup"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "ccdcdca2-4e77-4696-b0ff-45d13dbc6ac3",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import quimb.tensor\n",
"import datetime\n",
"import matplotlib.pyplot as plt\n",
"\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",
"\n",
"from rustworkx.visualization import graphviz_draw"
]
},
{
"cell_type": "markdown",
"id": "39bc4470-f7f8-4550-83a7-a22167024000",
"metadata": {},
"source": [
"## Part I. Small-scale example\n",
"\n",
"The first part of this tutorial uses a small-scale example with 10 sites to illustrate the process of mapping a quantum simulation problem to an executable quantum circuit. Here, well explore the dynamics of a 10-site XXZ model, allowing us to build and optimize a manageable quantum circuit before scaling to larger systems.\n",
"\n",
"The XXZ model is widely studied in physics for examining spin interactions and magnetic properties. We set up the Hamiltonian to have open boundary conditions with site-dependent interactions between neighboring sites along the chain.\n",
"\n",
"### Model Hamiltonian and observable\n",
"\n",
"The Hamiltonian for our 10-site XXZ model is defined as:\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 corresponding to edge $(i, i+1)$, and $L=10$ is the number of sites.\n",
"\n",
"By simulating the evolution of this system with reduced circuit depth, we can gain insights into using AQC-Tensor to compress and optimize circuits."
]
},
{
"cell_type": "markdown",
"id": "89f22428-d4d8-49e7-a1c5-39197dc58330",
"metadata": {},
"source": [
"#### Set up the Hamiltonian and observable\n",
"\n",
"Before we map our problem, we need to set up the coupling map, Hamiltonian, and observable for the 10-site XXZ model."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1ea0e102-23d5-4e6e-8ef8-e82843452b19",
"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/1ea0e102-23d5-4e6e-8ef8-e82843452b19-1.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# L is the number of sites, also the length of 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",
"# Generate an edge-coloring so we can make hw-efficient circuits\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",
"print(\"Hamiltonian:\", hamiltonian)\n",
"print(\"Observable:\", observable)\n",
"graphviz_draw(coupling_map.graph, method=\"circo\")"
]
},
{
"cell_type": "markdown",
"id": "e6cd54e3-6a9c-4cdc-8493-d612e930dcee",
"metadata": {},
"source": [
"With the Hamiltonian defined, we can proceed to construct the initial state."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "71252a74-e7bf-4003-a1fd-8f7659195f9a",
"metadata": {},
"outputs": [],
"source": [
"# Generate an initial state\n",
"initial_state = QuantumCircuit(L)\n",
"for i in range(L):\n",
" if i % 2:\n",
" initial_state.x(i)"
]
},
{
"cell_type": "markdown",
"id": "5fb34a14-d197-4ac4-a224-5be2cec06a2e",
"metadata": {},
"source": [
"### Step 1: Map classical inputs to a quantum problem\n",
"\n",
"Now that we have constructed the Hamiltonian, defining the spin-spin interactions and external magnetic fields that characterize the system, we follow three main steps in the AQC-Tensor workflow:\n",
"\n",
"1. **Generate the optimized AQC circuit**: Using Trotterization, we approximate the initial evolution, which is then compressed to reduce circuit depth.\n",
"2. **Create the remaining time evolution circuit**: Capture the evolution for the remaining time beyond the initial segment.\n",
"3. **Combine the circuits**: Merge the optimized AQC circuit with the remaining evolution circuit into a complete time-evolution circuit ready for execution.\n",
"\n",
"This approach creates a low-depth ansatz for the target evolution, supporting efficient simulation within near-term quantum hardware constraints."
]
},
{
"cell_type": "markdown",
"id": "7c5f2a2e-179c-4e62-a32f-5bc202f89701",
"metadata": {},
"source": [
"#### Determine the portion of time evolution to simulate classically\n",
"\n",
"Our goal is to simulate the time evolution of the model Hamiltonian defined earlier using Trotter evolution. To make this process efficient for quantum hardware, we split the evolution into two segments:\n",
"\n",
"- **Initial Segment**: This initial portion of the evolution, from $ t_i = 0.0 $ to $ t_f = 0.2 $, is simulable with MPS and can be efficiently “compiled” using AQC-Tensor. By using the [AQC-Tensor Qiskit addon](https://github.com/Qiskit/qiskit-addon-aqc-tensor), we generate a compressed circuit for this segment, referred to as the `aqc_target_circuit`. Because this segment will be simulated on a tensor-network simulator, we can afford to use a higher number of Trotter layers without impacting hardware resources significantly. We set `aqc_target_num_trotter_steps = 32` for this segment.\n",
"\n",
"- **Subsequent Segment**: This remaining portion of the evolution, from $ t = 0.2 $ to $ t = 0.4 $, will be executed on quantum hardware, referred to as the `subsequent_circuit`. Given hardware limitations, we aim to use as few Trotter layers as possible to maintain a manageable circuit depth. For this segment, we use `subsequent_num_trotter_steps = 3`.\n",
"\n",
"\n",
"#### Choose the split time\n",
"We choose $t = 0.2$ as the split time to balance classical simulability with hardware feasibility. Early in the evolution, entanglement in the XXZ model remains low enough for classical methods like MPS to approximate accurately.\n",
"\n",
"When choosing a split time, a good guideline is to select a point where entanglement is still manageable classically but captures enough of the evolution to simplify the hardware-executed portion. Trial and error may be needed to find the best balance for different Hamiltonians."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "199d4e7e-02b2-4da8-b567-8195fb9de536",
"metadata": {},
"outputs": [],
"source": [
"# Generate the AQC target circuit (initial segment)\n",
"aqc_evolution_time = 0.2\n",
"aqc_target_num_trotter_steps = 32\n",
"\n",
"aqc_target_circuit = initial_state.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": "code",
"execution_count": 5,
"id": "83039f82-97cb-4613-86c9-a8faf0839a02",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/83039f82-97cb-4613-86c9-a8faf0839a02-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Generate the subsequent circuit\n",
"subsequent_num_trotter_steps = 3\n",
"subsequent_evolution_time = 0.2\n",
"\n",
"subsequent_circuit = generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),\n",
" time=subsequent_evolution_time,\n",
")\n",
"subsequent_circuit.draw(\"mpl\", fold=-1)"
]
},
{
"cell_type": "markdown",
"id": "064a17af-4af8-4ff5-899c-750147d269df",
"metadata": {},
"source": [
"To enable a meaningful comparison, we will generate two additional circuits:\n",
"\n",
"- **AQC comparison circuit**: This circuit evolves up to `aqc_evolution_time` but uses the same Trotter step duration as the `subsequent_circuit`. It serves as a comparison to the `aqc_target_circuit`, showing the evolution we would observe without using an increased number of Trotter steps. We will refer to this circuit as the `aqc_comparison_circuit`.\n",
"\n",
"- **Reference circuit**: This circuit is used as a baseline to obtain the exact result. It simulates the full evolution using tensor networks to calculate the exact outcome, providing a reference for evaluating the effectiveness of AQC-Tensor. We will refer to this circuit as the `reference_circuit`."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "33b2ad93-7ae4-4250-bf38-9adc3bc2970d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of Trotter steps for comparison: 3\n"
]
}
],
"source": [
"# Generate the AQC comparison circuit\n",
"aqc_comparison_num_trotter_steps = int(\n",
" subsequent_num_trotter_steps\n",
" / subsequent_evolution_time\n",
" * aqc_evolution_time\n",
")\n",
"print(\n",
" \"Number of Trotter steps for comparison:\",\n",
" aqc_comparison_num_trotter_steps,\n",
")\n",
"\n",
"aqc_comparison_circuit = generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),\n",
" time=aqc_evolution_time,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "1bfa67a2-ac51-4158-b539-2c33f4c5ecf3",
"metadata": {},
"outputs": [],
"source": [
"# Generate the reference circuit\n",
"evolution_time = 0.4\n",
"reps = 200\n",
"\n",
"reference_circuit = initial_state.copy()\n",
"reference_circuit.compose(\n",
" generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=reps),\n",
" time=evolution_time,\n",
" ),\n",
" inplace=True,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "2051f622-5b71-4be5-aab9-e9eb5d315a6f",
"metadata": {},
"source": [
"#### Generate an ansatz and initial parameters from a Trotter circuit with fewer steps\n",
"\n",
"Now that we have constructed our four circuits, let's proceed with the AQC-Tensor workflow. First, we construct a “good” circuit that has the same evolution time as the target circuit, but with fewer Trotter steps (and thus fewer layers).\n",
"\n",
"Then we pass this “good” circuit to AQC-Tensors `generate_ansatz_from_circuit` function. This function analyzes the two-qubit connectivity of the circuit and returns two things:\n",
"\n",
"1. A general, parametrized ansatz circuit with the same two-qubit connectivity as the input circuit.\n",
"2. Parameters that, when plugged into the ansatz, yield the input (good) circuit.\n",
"\n",
"Soon we will take these parameters and iteratively adjust them to bring the ansatz circuit as close as possible to the target MPS."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "b9e81c51-dc6f-4237-9aca-e1384f1897bc",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/b9e81c51-dc6f-4237-9aca-e1384f1897bc-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"aqc_ansatz_num_trotter_steps = 1\n",
"\n",
"aqc_good_circuit = initial_state.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",
"aqc_ansatz.draw(\"mpl\", fold=-1)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "bd9e9bf2-9ee5-445d-aa4a-c7f412c488f8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"AQC Comparison circuit: depth 36\n",
"Target circuit: depth 385\n",
"Ansatz circuit: depth 7, with 156 parameters\n"
]
}
],
"source": [
"print(f\"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}\")\n",
"print(f\"Target circuit: depth {aqc_target_circuit.depth()}\")\n",
"print(\n",
" f\"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "3c8663b9-d513-4a2a-9911-03f3500ad486",
"metadata": {},
"source": [
"#### Choose settings for tensor network simulation\n",
"\n",
"Here, we use Quimb's matrix-product state circuit simulator, along with jax to provide the gradient."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "e5b9471f-fc7a-45c9-ab11-295cfff620bf",
"metadata": {},
"outputs": [],
"source": [
"simulator_settings = QuimbSimulator(\n",
" quimb.tensor.CircuitMPS, autodiff_backend=\"jax\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "5030d11d-54e3-4f29-acb2-6f7dd3ff05cb",
"metadata": {},
"source": [
"Next, we build a MPS representation of the target state that will be approximated using AQC-Tensor. This representation enables efficient handling of entanglement, providing a compact description of the quantum state for further optimization."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "1f050059-a281-41f1-a277-d7450e8b3ee3",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Target MPS maximum bond dimension: 5\n",
"Reference MPS maximum bond dimension: 7\n"
]
}
],
"source": [
"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",
"# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``\n",
"reference_mps = tensornetwork_from_circuit(\n",
" reference_circuit, simulator_settings\n",
")\n",
"reference_expval = reference_mps.local_expectation(\n",
" quimb.pauli(\"Z\") & quimb.pauli(\"Z\"), (L // 2 - 1, L // 2)\n",
").real.item()\n",
"print(\"Reference MPS maximum bond dimension:\", reference_mps.psi.max_bond())"
]
},
{
"cell_type": "markdown",
"id": "5ffbdd20-c9bf-4265-8a8d-8135abb2b8f7",
"metadata": {},
"source": [
"Note that, by choosing a larger number of Trotter steps for the target state, we have effectively reduced its Trotter error compared to the initial circuit. We can evaluate the fidelity ($ |\\langle \\psi_1 | \\psi_2 \\rangle|^2 $) between the state prepared by the initial circuit and the target state to quantify this difference."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "51d7d623-3e7c-44e7-80f1-94fd555a2c9e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting fidelity: 0.9982464959067222\n"
]
}
],
"source": [
"good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)\n",
"starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2\n",
"print(\"Starting fidelity:\", starting_fidelity)"
]
},
{
"cell_type": "markdown",
"id": "01234b5b-f4db-4e22-993a-c8673621ab7f",
"metadata": {},
"source": [
"#### Optimize the parameters of the ansatz using MPS calculations\n",
"In this step, we optimize the ansatz parameters by minimizing a simple cost function, `MaximizeStateFidelity`, using the L-BFGS optimizer from SciPy. We select a stopping criterion for the fidelity that ensures it surpasses the fidelity of the initial circuit without AQC-Tensor. Once this threshold is reached, the compressed circuit will exhibit both lower Trotter error and reduced depth compared to the original circuit. By using additional CPU time, further optimization can continue to increase fidelity."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "ad2265cb-19a6-4402-8b0d-24239c930c90",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2025-04-14 11:46:52.174235 Intermediate result: Fidelity 0.99795851\n",
"2025-04-14 11:46:52.218249 Intermediate result: Fidelity 0.99822826\n",
"2025-04-14 11:46:52.280924 Intermediate result: Fidelity 0.99829675\n",
"2025-04-14 11:46:52.356214 Intermediate result: Fidelity 0.99832474\n",
"2025-04-14 11:46:52.411609 Intermediate result: Fidelity 0.99836131\n",
"2025-04-14 11:46:52.453747 Intermediate result: Fidelity 0.99839954\n",
"2025-04-14 11:46:52.496184 Intermediate result: Fidelity 0.99846517\n",
"2025-04-14 11:46:52.542046 Intermediate result: Fidelity 0.99865029\n",
"2025-04-14 11:46:52.583679 Intermediate result: Fidelity 0.99872332\n",
"2025-04-14 11:46:52.628732 Intermediate result: Fidelity 0.99892359\n",
"2025-04-14 11:46:52.690386 Intermediate result: Fidelity 0.99900640\n",
"2025-04-14 11:46:52.759398 Intermediate result: Fidelity 0.99907169\n",
"2025-04-14 11:46:52.819496 Intermediate result: Fidelity 0.99911423\n",
"2025-04-14 11:46:52.884505 Intermediate result: Fidelity 0.99918716\n",
"2025-04-14 11:46:52.947919 Intermediate result: Fidelity 0.99921278\n",
"2025-04-14 11:46:53.012808 Intermediate result: Fidelity 0.99924853\n",
"2025-04-14 11:46:53.083626 Intermediate result: Fidelity 0.99928797\n",
"2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028\n",
"2025-04-14 11:46:53.221371 Intermediate result: Fidelity 0.99935757\n",
"2025-04-14 11:46:53.286211 Intermediate result: Fidelity 0.99938140\n",
"2025-04-14 11:46:53.352391 Intermediate result: Fidelity 0.99940964\n",
"2025-04-14 11:46:53.420472 Intermediate result: Fidelity 0.99944051\n",
"2025-04-14 11:46:53.486279 Intermediate result: Fidelity 0.99946828\n",
"2025-04-14 11:46:53.552338 Intermediate result: Fidelity 0.99948723\n",
"2025-04-14 11:46:53.618688 Intermediate result: Fidelity 0.99951011\n",
"2025-04-14 11:46:53.690878 Intermediate result: Fidelity 0.99954718\n",
"2025-04-14 11:46:53.762725 Intermediate result: Fidelity 0.99956267\n",
"2025-04-14 11:46:53.829784 Intermediate result: Fidelity 0.99958949\n",
"2025-04-14 11:46:53.897477 Intermediate result: Fidelity 0.99960498\n",
"2025-04-14 11:46:53.954633 Intermediate result: Fidelity 0.99961308\n",
"2025-04-14 11:46:54.010125 Intermediate result: Fidelity 0.99962894\n",
"2025-04-14 11:46:54.064717 Intermediate result: Fidelity 0.99964121\n",
"2025-04-14 11:46:54.118892 Intermediate result: Fidelity 0.99964348\n",
"2025-04-14 11:46:54.183236 Intermediate result: Fidelity 0.99964860\n",
"2025-04-14 11:46:54.245521 Intermediate result: Fidelity 0.99965695\n",
"2025-04-14 11:46:54.305792 Intermediate result: Fidelity 0.99966398\n",
"2025-04-14 11:46:54.355819 Intermediate result: Fidelity 0.99967816\n",
"2025-04-14 11:46:54.409580 Intermediate result: Fidelity 0.99968293\n",
"2025-04-14 11:46:54.457979 Intermediate result: Fidelity 0.99968936\n",
"2025-04-14 11:46:54.505891 Intermediate result: Fidelity 0.99969223\n",
"2025-04-14 11:46:54.551084 Intermediate result: Fidelity 0.99970009\n",
"2025-04-14 11:46:54.601817 Intermediate result: Fidelity 0.99970724\n",
"2025-04-14 11:46:54.650097 Intermediate result: Fidelity 0.99970987\n",
"2025-04-14 11:46:54.714727 Intermediate result: Fidelity 0.99971237\n",
"2025-04-14 11:46:54.780052 Intermediate result: Fidelity 0.99971916\n",
"2025-04-14 11:46:54.871994 Intermediate result: Fidelity 0.99971940\n",
"2025-04-14 11:46:54.958244 Intermediate result: Fidelity 0.99972465\n",
"2025-04-14 11:46:55.011057 Intermediate result: Fidelity 0.99972763\n",
"2025-04-14 11:46:55.175339 Intermediate result: Fidelity 0.99972894\n",
"2025-04-14 11:46:56.688912 Intermediate result: Fidelity 0.99972894\n",
"Done after 50 iterations.\n"
]
}
],
"source": [
"# Setting values for the optimization\n",
"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",
" # Good enough for now\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 (\n",
" result.status\n",
" not in (\n",
" 0,\n",
" 1,\n",
" 99,\n",
" )\n",
"): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration\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": "code",
"execution_count": 14,
"id": "c95ccea0-be99-4db9-838d-2327851e6761",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Final parameters: [-7.853983035039254, 1.5707966468427772, 1.5707962768868613, -1.570798010835122, 1.570794480409574, 1.5707972214146968, -1.570796593027083, 1.5707968206822998, -1.5707959018046258, -1.5707991700969144, 1.5707965852600927, 4.712386891737442, -7.853980840717957, 1.5707967508132654, 1.5707943162503217, -1.5707955382023582, 1.5707958007156742, 1.570796096113293, -1.5707928509846847, 1.5707971042943747, -1.570797909276557, -1.5707941020637393, 1.5707980179540793, 4.712389823219363, -1.5707928752386107, 1.5707996426312891, -1.5707975640471001, -1.570794132802984, 1.5707944361599957, 4.712390747060803, 0.1048818190315936, 0.06686710468840577, -0.0668645844756557, -3.1415923537135466, 1.2374931269696063, 6.323169390432535e-07, 3.53229204771738e-08, 2.1091105688681484, 6.283186439944202, 0.12152258846156239, 0.07961752617254866, -0.07961775088604585, -1.6564278051174865e-06, 2.0771163596472384, 3.141592651630471, -6.283185775192653, 1.7691609006726954, 3.1415922910116216, 0.19837572065074083, 0.11114901449078964, -0.11115124544944892, -3.141591983034976, 0.8570788408766729, 4.201601390404146e-07, -3.141593736550978, 0.34652010942396333, 6.283186232785291, 0.13606356527241956, 0.03891676349289617, -0.03891524189533726, -1.5707965732853424, 1.5707968967088564, -0.3086133992238162, 1.5707957152428194, 1.5707968398959653, -0.32062737993080026, 0.11027416939993417, 0.0726167290795046, -0.07262020423334464, -2.3729431959735024e-06, 1.8204437429254703, 9.299060301196612e-07, -3.141592899563451, 2.103269568939461, 3.1415937539734626, 0.11536891854817125, 0.09099022308254198, -0.09098864958606581, -3.1415913307373127, 2.078429034357281, -1.509777998069368e-06, -3.1415922600663255, 1.5189162645358172, -3.1415878461323583, 0.09999070991480716, 0.04352011445148391, -0.04351849541849812, -1.570797642506462, 1.570795238023824, 0.8903442644396505, 1.5707962698006606, 1.5707946765132268, 0.9098791754570567, 0.10448284343424026, 0.07317037684936827, -0.07316718173961152, -3.141592682240966, 2.1665363080039612, -7.450882112394189e-07, -5.771181304929921e-07, 2.615334999517103, -3.1415914971653898, 0.1890887078648001, 0.13578163074571992, -0.13578078143610256, 7.156734195912883e-07, 1.7915385305413096, -5.188866034727312e-07, 1.2827742939197711e-06, 1.2348316581417487, 6.28318357406372, 0.08061187643781703, 0.03820789039271876, -0.03820731868804904, 1.5707964027727628, 1.570798734462218, 4.387336153720882, -1.570795722044763, 1.570798457375325, 4.450361734163248, 0.092360147257953, 0.06047700345049011, -0.06048592856713045, -3.141591214829027, 2.6593289993286047, -2.366937342261038e-07, 8.112162974032695e-08, 1.8907014631413432, 8.355881261853104e-07, 0.23303641819370874, 0.14331998953606456, -0.1433194488304741, -3.141591621822901, 0.7455776479558791, 3.1415914520163586, -3.1415933560496105, 0.7603938554148255, -1.6230983177616282e-06, 0.07186349688535713, 0.03197144517771341, -0.031971177878588546, -4.712389048748508, 1.5707948403165752, 1.2773619319829186, -1.5707990802172127, 1.5707957676951863, 1.289083769394045, 0.13644999397718796, 0.032761460443590046, -0.032762060585195645, -1.5707977610073176, 1.5707964181578042, -3.4826435600366983, -4.712389691708343, 1.570794277502252, 2.799088046133275]\n"
]
}
],
"source": [
"parameters = [float(param) for param in aqc_final_parameters]\n",
"print(\"Final parameters:\", parameters)"
]
},
{
"cell_type": "markdown",
"id": "ca32c1ef-f7d5-4489-83b3-17972bf94700",
"metadata": {},
"source": [
"At this point, it is only necessary to find the final parameters to the ansatz circuit. We can then merge the optimized AQC circuit with the remaining evolution circuit to create a complete time-evolution circuit for execution on quantum hardware."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "813c9ced-6a2e-4345-bffc-7dae938e2015",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/813c9ced-6a2e-4345-bffc-7dae938e2015-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 15,
"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": "46a93127-5443-40b7-a81a-dfd70df42ba2",
"metadata": {},
"source": [
"We also need to merge our `aqc_comparison_circuit` with the remaining evolution circuit. This circuit will be used to compare the performance of the AQC-Tensor-optimized circuit with the original circuit."
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "86ba26ff-0bfa-47d0-b5ee-8944a8ddf274",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/86ba26ff-0bfa-47d0-b5ee-8944a8ddf274-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)\n",
"aqc_comparison_circuit.draw(\"mpl\", fold=-1)"
]
},
{
"cell_type": "markdown",
"id": "982c5067-9a74-42a0-bd31-aaff72cf79df",
"metadata": {},
"source": [
"### Step 2: Optimize problem for quantum hardware execution"
]
},
{
"cell_type": "markdown",
"id": "2121c40c-a9a7-4c36-a20a-04ee9f6637db",
"metadata": {},
"source": [
"Select the hardware. Here we will use any of the IBM Quantum&reg; devices available that have at least 127 qubits."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "88715b59-4516-4b75-b041-c978944dd14a",
"metadata": {},
"outputs": [],
"source": [
"service = QiskitRuntimeService()\n",
"backend = service.least_busy(min_num_qubits=127)\n",
"print(backend)"
]
},
{
"cell_type": "markdown",
"id": "3f477394-5eac-4c12-b36b-e94c800fa889",
"metadata": {},
"source": [
"We transpile PUBs (circuit and observables) to match the backend ISA (Instruction Set Architecture). By setting `optimization_level=3`, the transpiler optimizes the circuit to fit a one-dimensional chain of qubits, reducing the noise that impacts circuit fidelity. Once the circuits are transformed into a format compatible with the backend, we apply a corresponding transformation to the observables to ensure they align with the modified qubit layout."
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "087fff8d-98b9-4f9a-8004-01a3b0166e12",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],\n",
" coeffs=[1.+0.j])\n",
"Circuit depth: 111\n"
]
},
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/087fff8d-98b9-4f9a-8004-01a3b0166e12-1.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"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(\"Observable info:\", isa_observable)\n",
"print(\"Circuit depth:\", isa_circuit.depth())\n",
"isa_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
]
},
{
"cell_type": "markdown",
"id": "58e8e840-94a8-4414-81f7-01bbf1b73810",
"metadata": {},
"source": [
"Perform transpilation for the comparison circuit."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "7c2e5fe7-21ce-461d-adaa-776f8d882163",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],\n",
" coeffs=[1.+0.j])\n",
"Circuit depth: 158\n"
]
},
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/7c2e5fe7-21ce-461d-adaa-776f8d882163-1.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)\n",
"isa_comparison_observable = observable.apply_layout(\n",
" isa_comparison_circuit.layout\n",
")\n",
"print(\"Observable info:\", isa_comparison_observable)\n",
"print(\"Circuit depth:\", isa_comparison_circuit.depth())\n",
"isa_comparison_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
]
},
{
"cell_type": "markdown",
"id": "1f832aaa-c2a9-42b7-95b6-bd920cdc17b0",
"metadata": {},
"source": [
"### Step 3: Execute using Qiskit primitives\n",
"\n",
"In this step, we execute the transpiled circuit on quantum hardware (or a simulated backend). Using the `EstimatorV2` class from `qiskit_ibm_runtime`, we set up an estimator to run the circuit and measure the specified observable. The job result provides the expected outcome for the observable, giving us insights into the circuits performance on the target hardware."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "3aabf36b-4587-43b7-be99-9376fc7f47c1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Job ID: czyhqdxd8drg008hx0yg\n"
]
},
{
"data": {
"text/plain": [
"PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"estimator = Estimator(backend)\n",
"job = estimator.run([(isa_circuit, isa_observable)])\n",
"print(\"Job ID:\", job.job_id())\n",
"job.result()"
]
},
{
"cell_type": "markdown",
"id": "4ec6669b-2b76-41df-9634-3c073998b1d7",
"metadata": {},
"source": [
"Perform the execution for the comparison circuit."
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "91f16fff-da28-42b4-a32a-fa2f0945f8fd",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Job Comparison ID: czyhqdxd8drg008hx0yg\n"
]
},
{
"data": {
"text/plain": [
"PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])\n",
"print(\"Job Comparison ID:\", job.job_id())\n",
"job_comparison.result()"
]
},
{
"cell_type": "markdown",
"id": "3c6d16d4-3118-49b6-9594-3d7ddb02701c",
"metadata": {},
"source": [
"### Step 4: Post-process and return result in desired classical format\n",
"\n",
"In this case, reconstruction is unnecessary. We can directly examine the result by accessing the expectation value from the execution output."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "7247e56f-0ab8-4aa9-834c-ba95965a0b3f",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Exact: \t-0.5252\n",
"AQC: \t-0.4903, |∆| = 0.0349\n",
"AQC Comparison:\t0.5424, |∆| = 1.0676\n"
]
}
],
"source": [
"# AQC results\n",
"hw_results = job.result()\n",
"hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]\n",
"hw_expvals = [\n",
" pub_result_data[\"evs\"].tolist() for pub_result_data in hw_results_dicts\n",
"]\n",
"aqc_expval = hw_expvals[0]\n",
"\n",
"# AQC comparison results\n",
"hw_comparison_results = job_comparison.result()\n",
"hw_comparison_results_dicts = [\n",
" pub_result.data.__dict__ for pub_result in hw_comparison_results\n",
"]\n",
"hw_comparison_expvals = [\n",
" pub_result_data[\"evs\"].tolist()\n",
" for pub_result_data in hw_comparison_results_dicts\n",
"]\n",
"aqc_compare_expval = hw_comparison_expvals[0]\n",
"\n",
"print(f\"Exact: \\t{reference_expval:.4f}\")\n",
"print(\n",
" f\"AQC: \\t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}\"\n",
")\n",
"print(\n",
" f\"AQC Comparison:\\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "1c8b65e6-df72-45b3-8f31-a6aed44cb867",
"metadata": {},
"source": [
"Bar plot to compare the results of the AQC, comparison, and exact circuits."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "5f7b36a6-3666-4223-9c5d-d92bca741ad2",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/5f7b36a6-3666-4223-9c5d-d92bca741ad2-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"plt.style.use(\"seaborn-v0_8\")\n",
"\n",
"labels = [\"AQC Result\", \"AQC Comparison Result\"]\n",
"values = [abs(aqc_expval), abs(aqc_compare_expval)]\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"bars = plt.bar(labels, values, color=[\"tab:blue\", \"tab:purple\"])\n",
"plt.axhline(\n",
" y=abs(reference_expval), color=\"red\", linestyle=\"--\", label=\"Exact Result\"\n",
")\n",
"plt.xlabel(\"Results\")\n",
"plt.ylabel(\"Absolute Expected Value\")\n",
"plt.title(\"AQC Result vs AQC Comparison Result (Absolute Values)\")\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",
" round(y_val, 2),\n",
" va=\"bottom\",\n",
" )\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "ff81fb19-d175-42b0-8ef3-025712a7630d",
"metadata": {},
"source": [
"## Part II: scale it up\n",
"\n",
"\n",
"The second part of this tutorial builds on the previous example by scaling up to a larger system with 50 sites, illustrating how to map more complex quantum simulation problems to executable quantum circuits. Here, we explore the dynamics of a 50-site XXZ model, allowing us to build and optimize a substantial quantum circuit that reflects more realistic system sizes.\n",
"\n",
"The Hamiltonian for our 50-site XXZ model is defined as:\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 corresponding to edge $(i, i+1)$, and $L=50$ is the number of sites."
]
},
{
"cell_type": "markdown",
"id": "f6d11e17-b4be-44fa-bc09-465e5e66a6af",
"metadata": {},
"source": [
"Define the coupling map and edges for the Hamiltonian."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "d0dec1ee-85fe-4c40-9595-b7764e9cfcca",
"metadata": {},
"outputs": [],
"source": [
"L = 50 # L = length of our 1D spin chain\n",
"\n",
"# Generate the edge list for this spin-chain\n",
"edge_list = [(i - 1, i) for i in range(1, L)]\n",
"# Generate an edge-coloring so we can make hw-efficient circuits\n",
"even_edges = edge_list[::2]\n",
"odd_edges = edge_list[1::2]\n",
"\n",
"# Instantiate a CouplingMap object\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",
"\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",
"# Generate an initial state\n",
"L = hamiltonian.num_qubits\n",
"initial_state = QuantumCircuit(L)\n",
"for i in range(L):\n",
" if i % 2:\n",
" initial_state.x(i)"
]
},
{
"cell_type": "markdown",
"id": "65009d89-4752-40ac-b3ab-8e88425851b2",
"metadata": {},
"source": [
"### Step 1: Map classical inputs to a quantum problem\n",
"\n",
"For this larger problem, we start by constructing the Hamiltonian for the 50-site XXZ model, defining spin-spin interactions and external magnetic fields across all sites. After this, we follow three main steps:\n",
"\n",
"1. **Generate the optimized AQC circuit**: Use Trotterization to approximate the initial evolution, then compress this segment to reduce circuit depth.\n",
"2. **Create the remaining time evolution circuit**: Capture the remaining time evolution beyond the initial segment.\n",
"3. **Combine the circuits**: Merge the optimized AQC circuit with the remaining evolution circuit to create a complete time-evolution circuit ready for execution."
]
},
{
"cell_type": "markdown",
"id": "41f5186a-e54e-4913-8a52-1c90611991c7",
"metadata": {},
"source": [
"Generate the AQC target circuit (the initial segment)."
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "86735d08-49b3-47e4-abcd-631a77f50a02",
"metadata": {},
"outputs": [],
"source": [
"aqc_evolution_time = 0.2\n",
"aqc_target_num_trotter_steps = 32\n",
"\n",
"aqc_target_circuit = initial_state.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": "c5e79369-19ae-4b17-a9fc-9502a8a1254c",
"metadata": {},
"source": [
"Generate the subsequent circuit (the remaining segment)."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "35c6469c-3be7-4e89-a065-0e4957305b59",
"metadata": {},
"outputs": [],
"source": [
"subsequent_num_trotter_steps = 3\n",
"subsequent_evolution_time = 0.2\n",
"\n",
"subsequent_circuit = generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),\n",
" time=subsequent_evolution_time,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "0e13fbc5-0c00-4f21-9925-8d1230854dc5",
"metadata": {},
"source": [
"Generate the AQC comparison circuit (the initial segment, but with the same number of Trotter steps as the subsequent circuit)."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "922fb467-81b0-40d7-b21b-ef781ad043a1",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Number of Trotter steps for comparison: 3\n"
]
}
],
"source": [
"# Generate the AQC comparison circuit\n",
"aqc_comparison_num_trotter_steps = int(\n",
" subsequent_num_trotter_steps\n",
" / subsequent_evolution_time\n",
" * aqc_evolution_time\n",
")\n",
"print(\n",
" \"Number of Trotter steps for comparison:\",\n",
" aqc_comparison_num_trotter_steps,\n",
")\n",
"\n",
"aqc_comparison_circuit = generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),\n",
" time=aqc_evolution_time,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "21228ca8-3fa6-4efe-9e53-8d3f824b7729",
"metadata": {},
"source": [
"Generate the reference circuit."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "3525fe02-25cf-4254-b2c4-717f27bc29d8",
"metadata": {},
"outputs": [],
"source": [
"evolution_time = 0.4\n",
"reps = 200\n",
"\n",
"reference_circuit = initial_state.copy()\n",
"reference_circuit.compose(\n",
" generate_time_evolution_circuit(\n",
" hamiltonian,\n",
" synthesis=SuzukiTrotter(reps=reps),\n",
" time=evolution_time,\n",
" ),\n",
" inplace=True,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "a15b40ac-0259-4229-b76b-502ff7068499",
"metadata": {},
"source": [
"Generate an ansatz and initial parameters from a Trotter circuit with fewer steps."
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "ddbc3549-3171-4a29-b046-2a52b5e7065a",
"metadata": {},
"outputs": [],
"source": [
"aqc_ansatz_num_trotter_steps = 1\n",
"\n",
"aqc_good_circuit = initial_state.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",
")"
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "8e596d77-5954-4756-b765-48855cd38659",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"AQC Comparison circuit: depth 36\n",
"Target circuit: depth 385\n",
"Ansatz circuit: depth 7, with 816 parameters\n"
]
}
],
"source": [
"print(f\"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}\")\n",
"print(f\"Target circuit: depth {aqc_target_circuit.depth()}\")\n",
"print(\n",
" f\"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "4d07d63a-8572-4116-9fd5-c070f62043a7",
"metadata": {},
"source": [
"Set settings for tensor network simulation and then construct a matrix product state representation of the target state for optimization. Then, evaluate the fidelity between the initial circuit and the target state to quantify the difference in Trotter error."
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "6030706e-1451-47d9-9e47-bcccf4cb5d9c",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Target MPS maximum bond dimension: 5\n",
"Starting fidelity: 0.9926466919924161\n"
]
}
],
"source": [
"simulator_settings = QuimbSimulator(\n",
" quimb.tensor.CircuitMPS, autodiff_backend=\"jax\"\n",
")\n",
"\n",
"# Build the matrix-product representation of the state to be approximated by AQC\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",
"# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``\n",
"reference_mps = tensornetwork_from_circuit(\n",
" reference_circuit, simulator_settings\n",
")\n",
"reference_expval = reference_mps.local_expectation(\n",
" quimb.pauli(\"Z\") & quimb.pauli(\"Z\"), (L // 2 - 1, L // 2)\n",
").real.item()\n",
"\n",
"# Compute the starting fidelity\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(\"Starting fidelity:\", starting_fidelity)"
]
},
{
"cell_type": "markdown",
"id": "56dfc4e4-6b28-4ed1-81da-8b0f4bc99425",
"metadata": {},
"source": [
"To optimize the ansatz parameters, we minimize the `MaximizeStateFidelity` cost function using the L-BFGS optimizer from SciPy, with a stopping criterion set to surpass the fidelity of the initial circuit without AQC-Tensor. This ensures that the compressed circuit has both lower Trotter error and reduced depth."
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "af10ef7f-e57d-49a3-abb0-3d51e856c4b0",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"2025-04-14 11:48:28.705807 Intermediate result: Fidelity 0.99795851\n",
"2025-04-14 11:48:28.743265 Intermediate result: Fidelity 0.99822826\n",
"2025-04-14 11:48:28.776629 Intermediate result: Fidelity 0.99829675\n",
"2025-04-14 11:48:28.816153 Intermediate result: Fidelity 0.99832474\n",
"2025-04-14 11:48:28.856437 Intermediate result: Fidelity 0.99836131\n",
"2025-04-14 11:48:28.896432 Intermediate result: Fidelity 0.99839954\n",
"2025-04-14 11:48:28.936670 Intermediate result: Fidelity 0.99846517\n",
"2025-04-14 11:48:28.982069 Intermediate result: Fidelity 0.99865029\n",
"2025-04-14 11:48:29.026130 Intermediate result: Fidelity 0.99872332\n",
"2025-04-14 11:48:29.067426 Intermediate result: Fidelity 0.99892359\n",
"2025-04-14 11:48:29.110742 Intermediate result: Fidelity 0.99900640\n",
"2025-04-14 11:48:29.161362 Intermediate result: Fidelity 0.99907169\n",
"2025-04-14 11:48:29.207933 Intermediate result: Fidelity 0.99911423\n",
"2025-04-14 11:48:29.266772 Intermediate result: Fidelity 0.99918716\n",
"2025-04-14 11:48:29.331727 Intermediate result: Fidelity 0.99921278\n",
"2025-04-14 11:48:29.401694 Intermediate result: Fidelity 0.99924853\n",
"2025-04-14 11:48:29.467980 Intermediate result: Fidelity 0.99928797\n",
"2025-04-14 11:48:29.533281 Intermediate result: Fidelity 0.99933028\n",
"2025-04-14 11:48:29.600833 Intermediate result: Fidelity 0.99935757\n",
"2025-04-14 11:48:29.670816 Intermediate result: Fidelity 0.99938140\n",
"2025-04-14 11:48:29.736928 Intermediate result: Fidelity 0.99940964\n",
"2025-04-14 11:48:29.802931 Intermediate result: Fidelity 0.99944051\n",
"2025-04-14 11:48:29.869177 Intermediate result: Fidelity 0.99946828\n",
"2025-04-14 11:48:29.940156 Intermediate result: Fidelity 0.99948723\n",
"2025-04-14 11:48:30.005751 Intermediate result: Fidelity 0.99951011\n",
"2025-04-14 11:48:30.070853 Intermediate result: Fidelity 0.99954718\n",
"2025-04-14 11:48:30.139171 Intermediate result: Fidelity 0.99956267\n",
"2025-04-14 11:48:30.210506 Intermediate result: Fidelity 0.99958949\n",
"2025-04-14 11:48:30.279647 Intermediate result: Fidelity 0.99960498\n",
"2025-04-14 11:48:30.348016 Intermediate result: Fidelity 0.99961308\n",
"2025-04-14 11:48:30.414311 Intermediate result: Fidelity 0.99962894\n",
"2025-04-14 11:48:30.488910 Intermediate result: Fidelity 0.99964121\n",
"2025-04-14 11:48:30.561298 Intermediate result: Fidelity 0.99964348\n",
"2025-04-14 11:48:30.632214 Intermediate result: Fidelity 0.99964860\n",
"2025-04-14 11:48:30.705703 Intermediate result: Fidelity 0.99965695\n",
"2025-04-14 11:48:30.775679 Intermediate result: Fidelity 0.99966398\n",
"2025-04-14 11:48:30.842629 Intermediate result: Fidelity 0.99967816\n",
"2025-04-14 11:48:30.912357 Intermediate result: Fidelity 0.99968293\n",
"2025-04-14 11:48:30.979420 Intermediate result: Fidelity 0.99968936\n",
"2025-04-14 11:48:31.049196 Intermediate result: Fidelity 0.99969223\n",
"2025-04-14 11:48:31.125391 Intermediate result: Fidelity 0.99970009\n",
"2025-04-14 11:48:31.201256 Intermediate result: Fidelity 0.99970724\n",
"2025-04-14 11:48:31.272424 Intermediate result: Fidelity 0.99970987\n",
"2025-04-14 11:48:31.338907 Intermediate result: Fidelity 0.99971237\n",
"2025-04-14 11:48:31.404800 Intermediate result: Fidelity 0.99971916\n",
"2025-04-14 11:48:31.475226 Intermediate result: Fidelity 0.99971940\n",
"2025-04-14 11:48:31.547746 Intermediate result: Fidelity 0.99972465\n",
"2025-04-14 11:48:31.622827 Intermediate result: Fidelity 0.99972763\n",
"2025-04-14 11:48:31.819516 Intermediate result: Fidelity 0.99972894\n",
"2025-04-14 11:48:33.444538 Intermediate result: Fidelity 0.99972894\n",
"Done after 50 iterations.\n"
]
}
],
"source": [
"# Setting values for the optimization\n",
"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",
" # Good enough for now\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 (\n",
" result.status\n",
" not in (\n",
" 0,\n",
" 1,\n",
" 99,\n",
" )\n",
"): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration\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": "code",
"execution_count": 39,
"id": "0e711fcc-7767-411e-bb73-fd9c8408a22b",
"metadata": {},
"outputs": [],
"source": [
"parameters = [float(param) for param in aqc_final_parameters]"
]
},
{
"cell_type": "markdown",
"id": "b1838b8e-81d4-4897-8cbd-dd7daa8bb220",
"metadata": {},
"source": [
"Construct the final circuit for transpilation by assembling the optimized ansatz with the remaining time evolution circuit."
]
},
{
"cell_type": "code",
"execution_count": 40,
"id": "f96b1ab0-f0ed-485f-a763-4ab57d36f410",
"metadata": {},
"outputs": [],
"source": [
"aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)\n",
"aqc_final_circuit.compose(subsequent_circuit, inplace=True)"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "579cbef1-a3a8-4916-b840-ab3fcc5c4b33",
"metadata": {},
"outputs": [],
"source": [
"aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)"
]
},
{
"cell_type": "markdown",
"id": "bb111fb1-07e4-4f0c-ad71-933b04492a23",
"metadata": {},
"source": [
"### Step 2: Optimize problem for quantum hardware execution"
]
},
{
"cell_type": "markdown",
"id": "627dd426-730d-4554-b631-1e9949f46c26",
"metadata": {},
"source": [
"Select the backend."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "771a44a6-b5ba-4b9d-8c64-9a79f6d4ed77",
"metadata": {},
"outputs": [],
"source": [
"service = QiskitRuntimeService()\n",
"backend = service.least_busy(min_num_qubits=127)\n",
"print(backend)"
]
},
{
"cell_type": "markdown",
"id": "cbc5d688-9acf-4fe2-8d58-fea74edb09e8",
"metadata": {},
"source": [
"Transpile the completed circuit on the target hardware, preparing it for execution. The resulting ISA circuit can then be sent for execution on the backend."
]
},
{
"cell_type": "code",
"execution_count": 43,
"id": "85b4acc0-7121-416d-9bf5-b6d3135ae805",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
" coeffs=[1.+0.j])\n",
"Circuit depth: 122\n"
]
},
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/85b4acc0-7121-416d-9bf5-b6d3135ae805-1.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"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(\"Observable info:\", isa_observable)\n",
"print(\"Circuit depth:\", isa_circuit.depth())\n",
"isa_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
]
},
{
"cell_type": "code",
"execution_count": 44,
"id": "b0d295c7-c816-4683-bb2a-0ce9898e5d88",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
" coeffs=[1.+0.j])\n",
"Circuit depth: 158\n"
]
},
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/b0d295c7-c816-4683-bb2a-0ce9898e5d88-1.avif\" alt=\"Output of the previous code cell\" />"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)\n",
"isa_comparison_observable = observable.apply_layout(\n",
" isa_comparison_circuit.layout\n",
")\n",
"print(\"Observable info:\", isa_comparison_observable)\n",
"print(\"Circuit depth:\", isa_comparison_circuit.depth())\n",
"isa_comparison_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
]
},
{
"cell_type": "markdown",
"id": "f9431bec-66d1-44e3-a77d-01ca9e03e523",
"metadata": {},
"source": [
"### Step 3: Execute using Qiskit primitives\n",
"\n",
"In this step, we run the transpiled circuit on quantum hardware (or a simulated backend) using `EstimatorV2` from `qiskit_ibm_runtime` to measure the specified observable. The job result will provide valuable insights into the circuits performance on the target hardware.\n",
"\n",
"For this larger-scale example, we will explore how to utilize `EstimatorOptions` to better manage and control the parameters of our hardware experiment. While these settings are optional, they are useful for tracking experiment parameters and refining execution options for optimal results.\n",
"\n",
"For a complete list of available execution options, refer to the [qiskit-ibm-runtime documentation](/docs/api/qiskit-ibm-runtime/options-estimator-options)."
]
},
{
"cell_type": "code",
"execution_count": 45,
"id": "cf449353-f392-4248-b6ad-5af9107c1fff",
"metadata": {},
"outputs": [],
"source": [
"twirling_options = {\n",
" \"enable_gates\": True,\n",
" \"enable_measure\": True,\n",
" \"num_randomizations\": 300,\n",
" \"shots_per_randomization\": 100,\n",
" \"strategy\": \"active\",\n",
"}\n",
"\n",
"zne_options = {\n",
" \"amplifier\": \"gate_folding\",\n",
" \"noise_factors\": [1, 2, 3],\n",
" \"extrapolated_noise_factors\": list(np.linspace(0, 3, 31)),\n",
" \"extrapolator\": [\"exponential\", \"linear\", \"fallback\"],\n",
"}\n",
"\n",
"meas_learning_options = {\n",
" \"num_randomizations\": 512,\n",
" \"shots_per_randomization\": 512,\n",
"}\n",
"\n",
"resilience_options = {\n",
" \"measure_mitigation\": True,\n",
" \"zne_mitigation\": True,\n",
" \"zne\": zne_options,\n",
" \"measure_noise_learning\": meas_learning_options,\n",
"}\n",
"\n",
"estimator_options = {\n",
" \"resilience\": resilience_options,\n",
" \"twirling\": twirling_options,\n",
"}\n",
"\n",
"estimator = Estimator(backend, options=estimator_options)"
]
},
{
"cell_type": "code",
"execution_count": 46,
"id": "ea2a1425-a49b-4b31-b019-abbee4fdf690",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Job ID: czyjx6crxz8g008f63r0\n"
]
},
{
"data": {
"text/plain": [
"PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job = estimator.run([(isa_circuit, isa_observable)])\n",
"print(\"Job ID:\", job.job_id())\n",
"job.result()"
]
},
{
"cell_type": "code",
"execution_count": 47,
"id": "4f7d9e7d-9da1-4cc3-ae09-0e3613c5479a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Job Comparison ID: czyjx6crxz8g008f63r0\n"
]
},
{
"data": {
"text/plain": [
"PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])\n",
"print(\"Job Comparison ID:\", job.job_id())\n",
"job_comparison.result()"
]
},
{
"cell_type": "markdown",
"id": "17ced13b-a3d3-4ded-92a6-b978508dd27a",
"metadata": {},
"source": [
"### Step 4: Post-process and return result in desired classical format\n",
"Here, no reconstruction is needed, like before; we can directly access the expectation value from the execution output to examine the result."
]
},
{
"cell_type": "code",
"execution_count": 48,
"id": "5b2e2d3e-bebb-44a4-bbdb-881ffc2749e5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Exact: \t-0.5888\n",
"AQC: \t-0.4809, |∆| = 0.1078\n",
"AQC Comparison:\t1.1764, |∆| = 1.7652\n"
]
}
],
"source": [
"# AQC results\n",
"hw_results = job.result()\n",
"hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]\n",
"hw_expvals = [\n",
" pub_result_data[\"evs\"].tolist() for pub_result_data in hw_results_dicts\n",
"]\n",
"aqc_expval = hw_expvals[0]\n",
"\n",
"# AQC comparison results\n",
"hw_comparison_results = job_comparison.result()\n",
"hw_comparison_results_dicts = [\n",
" pub_result.data.__dict__ for pub_result in hw_comparison_results\n",
"]\n",
"hw_comparison_expvals = [\n",
" pub_result_data[\"evs\"].tolist()\n",
" for pub_result_data in hw_comparison_results_dicts\n",
"]\n",
"aqc_compare_expval = hw_comparison_expvals[0]\n",
"\n",
"print(f\"Exact: \\t{reference_expval:.4f}\")\n",
"print(\n",
" f\"AQC: \\t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}\"\n",
")\n",
"print(\n",
" f\"AQC Comparison:\\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}\"\n",
")"
]
},
{
"cell_type": "markdown",
"id": "f5ba5634-1689-457a-b7c7-adf3ca8e1d41",
"metadata": {},
"source": [
"Plot the results of the AQC, comparison, and exact circuits for the 50-site XXZ model."
]
},
{
"cell_type": "code",
"execution_count": 49,
"id": "01889c4d-16a4-458a-9211-08be8bcae1e4",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<Image src=\"/docs/images/tutorials/approximate-quantum-compilation-for-time-evolution/extracted-outputs/01889c4d-16a4-458a-9211-08be8bcae1e4-0.avif\" alt=\"Output of the previous code cell\" />"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"labels = [\"AQC Result\", \"AQC Comparison Result\"]\n",
"values = [abs(aqc_expval), abs(aqc_compare_expval)]\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"bars = plt.bar(labels, values, color=[\"tab:blue\", \"tab:purple\"])\n",
"plt.axhline(\n",
" y=abs(reference_expval), color=\"red\", linestyle=\"--\", label=\"Exact Result\"\n",
")\n",
"plt.xlabel(\"Results\")\n",
"plt.ylabel(\"Absolute Expected Value\")\n",
"plt.title(\"AQC Result vs AQC Comparison Result (Absolute Values)\")\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",
" round(y_val, 2),\n",
" va=\"bottom\",\n",
" )\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "45f512f7-9233-4f1f-8f24-cb3524567135",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"This tutorial demonstrated how to use Approximate Quantum Compilation with tensor networks (AQC-Tensor) to compress and optimize circuits for simulating quantum dynamics at scale. Utilizing both small and large Heisenberg models, we applied AQC-Tensor to reduce the circuit depth required for Trotterized time evolution. By generating a parametrized ansatz from a simplified Trotter circuit and optimizing it with matrix product state (MPS) techniques, we achieved a low-depth approximation of the target evolution that is both accurate and efficient.\n",
"\n",
"The workflow here highlights the key advantages of AQC-Tensor for scaling quantum simulations:\n",
"\n",
"- **Significant circuit compression**: AQC-Tensor reduced the circuit depth needed for complex time evolution, enhancing its feasibility on current devices.\n",
"- **Efficient optimization**: The MPS approach provided a robust framework for parameter optimization, balancing fidelity with computational efficiency.\n",
"- **Hardware-ready execution**: Transpiling the final optimized circuit ensured it met the constraints of the target quantum hardware.\n",
"\n",
"As larger quantum devices and more advanced algorithms emerge, techniques like AQC-Tensor will become essential for running complex quantum simulations on near-term hardware, demonstrating promising progress in managing depth and fidelity for scalable quantum applications."
]
},
{
"cell_type": "markdown",
"id": "3cc40a5a-4b55-45e8-a4f1-df45b9e37abd",
"metadata": {},
"source": [
"## Tutorial survey\n",
"\n",
"Please take one minute to provide feedback on this tutorial. Your insights will help us improve our content offerings and user experience.\n",
"\n",
"[Link to survey](https://your.feedback.ibm.com/jfe/form/SV_eF01c2sfeSt6cqq)"
]
},
{
"cell_type": "markdown",
"id": "07d2d65e-d1cf-433a-a1a4-31976c5733d1",
"metadata": {},
"source": [
"© IBM Corp. 2025"
]
}
],
"metadata": {
"description": "This tutorial demonstrates how to implement AQC-Tensor with Qiskit to enhance quantum circuit performance",
"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"
},
"platform": "cloud",
"title": "Approximate quantum compilation for time evolution circuits"
},
"nbformat": 4,
"nbformat_minor": 4
}