diff --git a/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits.ipynb b/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits.ipynb index 5805af0929e..5f204c56594 100644 --- a/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits.ipynb +++ b/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits.ipynb @@ -2,74 +2,92 @@ "cells": [ { "cell_type": "markdown", - "id": "f056ca4a-fbe4-4051-bbcd-79c2d7848cd0", + "id": "a1b2c3d4", "metadata": {}, "source": [ "---\n", "title: Compilation methods for Hamiltonian simulation circuits\n", - "description: This tutorial provides a comparative overview of three compilation methods in Qiskit for Hamiltonian simulation workloads.\n", + "description: Compare SABRE, AI transpiler, and Rustiq compilation methods on Hamiltonian simulation circuits from Hamlib.\n", "---\n", "\n", "\n", "# Compilation methods for Hamiltonian simulation circuits\n", - "Estimated QPU usage: no execution was done in this tutorial because it is focused on the transpilation process.\n", + "*Usage estimate: less than 1 minute on IBM Heron*\n", "\n", - "{/* cspell:ignore Rustiq, nshuffles, edgecolors, edgecolor, Hamlib, Benchpress, Brugière, Goubault, Martiel, Dubal, Lishman, Ivrii, fontweight, fontsize, textprops, wedgeprops, startangle, autopct */}" + "{/* cspell:ignore Rustiq, nshuffles, edgecolors, edgecolor, Hamlib, Benchpress, Brugiere, Goubault, Martiel, Dubal, Lishman, Ivrii, fontweight, fontsize, textprops, wedgeprops, startangle, autopct, Hellinger, iloc, ylabel, frameon, ylims, CMHSC */}" ] }, { "cell_type": "markdown", - "id": "d960a90b-1487-4310-a222-b95b8a77a080", + "id": "b1c2d3e4", + "metadata": {}, + "source": [ + "## Learning outcomes\n", + "After going through this tutorial, you will understand:\n", + "- How to use the Qiskit transpiler with SABRE for layout and routing optimization\n", + "- How to leverage the AI transpiler for advanced circuit optimization\n", + "- How to use the Rustiq plugin for synthesizing `PauliEvolutionGate` operations in Hamiltonian simulation circuits\n", + "- How to benchmark and compare compilation methods using two-qubit depth, total gate count, and runtime\n", + "\n", + "## Prerequisites\n", + "We suggest that you are familiar with the following topics before going through this tutorial:\n", + "- [Transpilation concepts](https://quantum.cloud.ibm.com/docs/en/guides/transpile)\n", + "- [Transpiler stages](https://quantum.cloud.ibm.com/docs/en/guides/transpiler-stages)\n", + "- [Transpile with pass managers](https://quantum.cloud.ibm.com/docs/en/guides/transpile-with-pass-managers)" + ] + }, + { + "cell_type": "markdown", + "id": "c1d2e3f4", "metadata": {}, "source": [ "## Background\n", "\n", - "Quantum circuit compilation is a crucial step in the quantum computing workflow. It involves transforming a high-level quantum algorithm into a physical quantum circuit that adheres to the constraints of the target quantum hardware. Effective compilation can significantly impact the performance of quantum algorithms by reducing circuit depth, gate count, and execution time. This tutorial explores three distinct approaches to quantum circuit compilation in Qiskit, showcasing their strengths and applications through practical examples.\n", + "Quantum circuit compilation transforms a high-level quantum algorithm into a physical circuit that respects the constraints of the target hardware. Effective compilation can significantly reduce circuit depth and gate count, both of which directly impact the quality of results on near-term quantum devices.\n", "\n", - "The goal of this tutorial is to teach users how to apply and evaluate three compilation methods in Qiskit: the SABRE transpiler, the AI-powered transpiler, and the Rustiq plugin. Users will learn how to use each method effectively and how to benchmark their performance across different quantum circuits. By the end of this tutorial, users will be able to choose and tailor compilation strategies based on specific optimization goals such as reducing circuit depth, minimizing gate count, or improving runtime.\n", + "This tutorial benchmarks three compilation methods on Hamiltonian simulation circuits built with `PauliEvolutionGate`. These circuits model pairwise qubit interactions (such as $ZZ$, $XX$, and $YY$ terms) and are common in quantum chemistry, condensed matter physics, and materials science.\n", "\n", - "### What you will learn\n", - "- **How to use the Qiskit transpiler with SABRE for layout and routing optimization.**\n", - "- **How to leverage the AI transpiler for advanced, automated circuit optimization.**\n", - "- **How to employ the Rustiq plugin for circuits requiring precise synthesis of operations, particularly in Hamiltonian simulation tasks.**\n", - "\n", - "This tutorial uses three example circuits following the [Qiskit patterns](/docs/guides/intro-to-patterns) workflow to illustrate the performance of each compilation method. By the end of this tutorial, users will be equipped to choose the appropriate compilation strategy based on their specific requirements and constraints.\n", + "The benchmark circuits come from the [Hamlib](https://github.com/SRI-International/QC-App-Oriented-Benchmarks/tree/master/hamlib) collection, accessed through the [Benchpress](https://github.com/Qiskit/benchpress) repository. Hamlib provides a standardized set of representative Hamiltonians, making it possible to compare compilation strategies on realistic simulation workloads.\n", "\n", "### Compilation methods overview\n", "\n", - "#### 1. **Qiskit transpiler with SABRE**\n", - "The Qiskit transpiler uses the SABRE (SWAP-based BidiREctional heuristic search) algorithm to optimize circuit layout and routing. SABRE focuses on minimizing SWAP gates and their impact on circuit depth while adhering to hardware connectivity constraints. This method is highly versatile and suitable for general-purpose circuit optimization, providing a balance between performance and computation time. To take advantage of the latest improvements in SABRE, detailed in [\\[1\\]](https://arxiv.org/abs/2409.08368), you can increase the number of trials (for example, `layout_trials=400, swap_trials=400`). For the purposes of this tutorial, we will use the default values for the number of trials in order to compare to Qiskit's default transpiler. The advantages and parameter exploration of SABRE are covered in a separate [deep-dive tutorial](/docs/tutorials/transpilation-optimizations-with-sabre).\n", - "\n", - "#### 2. **AI transpiler**\n", + "#### Qiskit transpiler with SABRE\n", + "The Qiskit transpiler uses the SABRE (SWAP-based BidiREctional heuristic search) algorithm to optimize circuit layout and routing. SABRE focuses on minimizing SWAP gates and their impact on circuit depth while respecting hardware connectivity constraints. It is a general-purpose method that provides a good balance between performance and compilation time. For more details, see [\\[1\\]](https://arxiv.org/abs/2409.08368). The advantages and parameter exploration of SABRE are covered in a separate [deep-dive tutorial](https://quantum.cloud.ibm.com/docs/en/tutorials/transpilation-optimizations-with-sabre).\n", "\n", - "The AI-powered transpiler in Qiskit uses machine learning to predict optimal transpilation strategies by analyzing patterns in circuit structure and hardware constraints to select the best sequence of optimizations for a given input. This method is particularly effective for large-scale quantum circuits, offering a high degree of automation and adaptability to diverse problem types. In addition to general circuit optimization, the AI transpiler can be used with the `AIPauliNetworkSynthesis` pass, which targets Pauli network circuits — blocks composed of H, S, SX, CX, RX, RY, and RZ gates — and applies a reinforcement learning-based synthesis approach. For more information on the AI transpiler and its synthesis strategies, see [\\[2\\]](https://arxiv.org/abs/2405.13196) and [\\[3\\]](https://arxiv.org/abs/2503.14448).\n", + "#### AI transpiler\n", + "The AI-powered transpiler uses machine learning to predict optimal transpilation strategies by analyzing patterns in circuit structure and hardware constraints. It can also apply the `AIPauliNetworkSynthesis` pass, which targets Pauli network circuits using a reinforcement learning-based synthesis approach. For more information, see [\\[2\\]](https://arxiv.org/abs/2405.13196) and [\\[3\\]](https://arxiv.org/abs/2503.14448).\n", "\n", + "#### Rustiq plugin\n", + "The Rustiq plugin provides advanced synthesis techniques specifically for `PauliEvolutionGate` operations, which represent Pauli rotations commonly used in Trotterized dynamics. It is designed to produce low-depth circuit decompositions for Hamiltonian simulation workloads. For more details, see [\\[4\\]](https://arxiv.org/abs/2404.03280).\n", "\n", + "### Key metrics\n", "\n", - "#### 3. **Rustiq plugin**\n", - "The Rustiq plugin introduces advanced synthesis techniques specifically for `PauliEvolutionGate` operations, which represent Pauli rotations commonly used in Trotterized dynamics. This plugin is valuable for circuits implementing Hamiltonian simulation, such as those used in quantum chemistry and physics problems, where accurate Pauli rotations are essential for simulating problem Hamiltonians effectively. Rustiq offers precise, low-depth circuit synthesis for these specialized operations. For more details about the implementation and performance of Rustiq, please refer to [\\[4\\]](https://arxiv.org/abs/2404.03280).\n", - "\n", - "By exploring these compilation methods in depth, this tutorial provides users with the tools to enhance the performance of their quantum circuits, paving the way for more efficient and practical quantum computations." + "We compare the three methods on the following metrics:\n", + "- **Two-qubit depth**: The depth of the circuit counting only two-qubit gates. This is often the bottleneck for fidelity on real hardware.\n", + "- **Circuit size (total gate count)**: The total number of gates in the transpiled circuit.\n", + "- **Runtime**: The wall-clock time for transpilation." ] }, { "cell_type": "markdown", - "id": "53c589f4-c63c-47f3-8642-1f189e445307", + "id": "d1e2f3a4", "metadata": {}, "source": [ "## Requirements\n", "\n", "Before starting this tutorial, be sure you have the following installed:\n", - "- Qiskit SDK v1.3 or later, with [visualization](/docs/api/qiskit/visualization) support\n", - "- Qiskit Runtime v0.28 or later (`pip install qiskit-ibm-runtime`)\n", + "\n", + "- Qiskit SDK v2.0 or later, with [visualization](/docs/api/qiskit/visualization) support\n", + "- Qiskit Runtime v0.22 or later (`pip install qiskit-ibm-runtime`)\n", + "- Qiskit Aer (`pip install qiskit-aer`)\n", "- Qiskit IBM Transpiler (`pip install qiskit-ibm-transpiler`)\n", "- Qiskit AI Transpiler local mode (`pip install qiskit_ibm_ai_local_transpiler`)\n", - "- Networkx graph library (`pip install networkx`)" + "- Networkx (`pip install networkx`)" ] }, { "cell_type": "markdown", - "id": "4f79e4a8-48bc-4af8-a172-f0057fa851eb", + "id": "e1f2a3b4", "metadata": {}, "source": [ "## Setup" @@ -78,184 +96,131 @@ { "cell_type": "code", "execution_count": 1, - "id": "1ecd9900-e511-486e-97be-aac5f75b1917", + "id": "f1a2b3c4", "metadata": {}, "outputs": [], "source": [ "from qiskit.circuit import QuantumCircuit\n", - "from qiskit_ibm_runtime import QiskitRuntimeService\n", - "from qiskit.circuit.library import (\n", - " efficient_su2,\n", - " PauliEvolutionGate,\n", - ")\n", + "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n", + "from qiskit.circuit.library import PauliEvolutionGate\n", "from qiskit_ibm_transpiler import generate_ai_pass_manager\n", "from qiskit.quantum_info import SparsePauliOp\n", "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n", "from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit_aer.noise import NoiseModel, depolarizing_error\n", "from collections import Counter\n", - "from IPython.display import display\n", + "from scipy.sparse import SparseEfficiencyWarning\n", "import time\n", + "import warnings\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", + "import matplotlib.ticker as ticker\n", "import numpy as np\n", "import json\n", "import requests\n", "import logging\n", "\n", - "# Suppress noisy loggers\n", + "# Suppress noisy loggers and warnings\n", "logging.getLogger(\n", " \"qiskit_ibm_transpiler.wrappers.ai_local_synthesis\"\n", ").setLevel(logging.ERROR)\n", + "warnings.filterwarnings(\"ignore\", category=FutureWarning)\n", + "warnings.filterwarnings(\"ignore\", category=SparseEfficiencyWarning)\n", "\n", "seed = 42 # Seed for reproducibility" ] }, { "cell_type": "markdown", - "id": "c3a31b4d-d679-4657-b372-ba19bcaf8eca", - "metadata": {}, - "source": [ - "## Part 1: Efficient SU2 Circuit\n", - "\n", - "### Step 1: Map classical inputs to a quantum problem\n", - "\n", - "In this section, we explore the `efficient_su2` circuit, a hardware-efficient ansatz commonly used in variational quantum algorithms (such as VQE) and quantum machine-learning tasks. The circuit consists of alternating layers of single-qubit rotations and entangling gates arranged in a circular pattern, designed to explore the quantum state space effectively while maintaining manageable depth.\n", - "\n", - "We will begin by constructing one `efficient_su2` circuit to demonstrate how to compare different compilation methods. After Part 1, we will expand our analysis to a larger set of circuits, enabling a comprehensive benchmark for evaluating the performance of various compilation techniques." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "f362cdac-94d8-4cc5-85f4-015c3d9eba3a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "qubit_size = list(range(10, 101, 10))\n", - "qc_su2_list = [\n", - " efficient_su2(n, entanglement=\"circular\", reps=1)\n", - " .decompose()\n", - " .copy(name=f\"SU2_{n}\")\n", - " for n in qubit_size\n", - "]\n", - "\n", - "# Draw the first circuit\n", - "qc_su2_list[0].draw(output=\"mpl\")" - ] - }, - { - "cell_type": "markdown", - "id": "d6671456-9b17-42bb-b94a-d42a29e6fad9", + "id": "a2b3c4d5", "metadata": {}, "source": [ - "### Step 2: Optimize problem for quantum hardware execution\n", + "### Connect to a backend\n", "\n", - "This step is the main focus of the tutorial. Here, we aim to optimize quantum circuits for efficient execution on real quantum hardware. Our primary objective is to reduce circuit depth and gate count, which are key factors in improving execution fidelity and mitigating hardware noise.\n", - "\n", - "- **SABRE transpiler**: Uses Qiskit’s default transpiler with the SABRE layout and routing algorithm.\n", - "- **AI transpiler (local mode)**: The standard AI-powered transpiler using local inference and the default synthesis strategy.\n", - "- **Rustiq plugin**: A transpiler plugin designed for low-depth compilation tailored to Hamiltonian simulation tasks.\n", - "\n", - "The goal of this step is to compare the results of these methods in terms of the transpiled circuit’s depth and gate count. Another important metric we consider is the transpilation runtime. By analyzing these metrics, we can evaluate the relative strengths of each method and determine which produces the most efficient circuit for execution on the selected hardware.\n", - "\n", - "Note: For the initial SU2 circuit example, we will only compare the SABRE transpiler to the default AI transpiler. However, in the subsequent benchmark using Hamlib circuits, we will compare all three transpilation methods." + "Select a backend that will be used for both the small-scale and large-scale examples. The backend determines the coupling map and basis gates that the transpiler targets." ] }, { "cell_type": "code", "execution_count": null, - "id": "c1ce1ad9-d529-49a1-91df-b540603ceb88", + "id": "b2c3d4e5", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "qiskit_runtime_service._get_crn_from_instance_name:WARNING:2025-07-30 21:46:30,843: Multiple instances found. Using all matching instances.\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "Using backend: \n" + "Using backend: \n" ] } ], "source": [ "# QiskitRuntimeService.save_account(channel=\"ibm_quantum_platform\", token=\"\", overwrite=True, set_as_default=True)\n", "service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n", - "backend = service.backend(\"ibm_torino\")\n", + "backend = service.least_busy(operational=True, simulator=False)\n", "print(f\"Using backend: {backend}\")" ] }, { "cell_type": "markdown", - "id": "381cfaca-103b-41bc-b3b3-f76808f44638", + "id": "c2d3e4f5", "metadata": {}, "source": [ - "Qiskit transpiler with SABRE:" + "### Define pass managers\n", + "\n", + "Set up the three compilation methods. Each pass manager targets the same backend so the comparisons are fair." ] }, { "cell_type": "code", - "execution_count": 4, - "id": "92a8f12d-3f97-400d-b6b6-a8c717f9ff0f", + "execution_count": 3, + "id": "d2e3f4a5", "metadata": {}, "outputs": [], "source": [ + "# SABRE pass manager (Qiskit default at optimization level 3)\n", "pm_sabre = generate_preset_pass_manager(\n", " optimization_level=3, backend=backend, seed_transpiler=seed\n", ")" ] }, - { - "cell_type": "markdown", - "id": "d1ae6e70-f276-41f3-8e40-68a9523eae06", - "metadata": {}, - "source": [ - "AI transpiler:" - ] - }, { "cell_type": "code", - "execution_count": 5, - "id": "6c27960f-e1cd-4cb5-b807-4dace42ea970", + "execution_count": 4, + "id": "e2f3a4b5", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "ba4c769273da49eda324d21f66c455b4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Fetching 127 files: 0%| | 0/127 [00:00=20 qubits): 22\n" ] - }, + } + ], + "source": [ + "qc_small = [qc for qc in qc_ham_list if qc.num_qubits < 20]\n", + "qc_large = [qc for qc in qc_ham_list if qc.num_qubits >= 20]\n", + "\n", + "print(f\"Small-scale circuits (<20 qubits): {len(qc_small)}\")\n", + "print(f\"Large-scale circuits (>=20 qubits): {len(qc_large)}\")" + ] + }, + { + "cell_type": "markdown", + "id": "a4b5c6d7", + "metadata": {}, + "source": [ + "Preview one of the small-scale Hamiltonian circuits before transpilation." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "b4c5d6e7", + "metadata": {}, + "outputs": [ { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, + "execution_count": 11, "metadata": {}, - "output_type": "display_data" - }, + "output_type": "execute_result" + } + ], + "source": [ + "# We decompose the circuit here, otherwise it would just be a PauliEvolutionGate box,\n", + "# which isn't very informative to look at!\n", + "qc_small[0].decompose().draw(\"mpl\", fold=-1)" + ] + }, + { + "cell_type": "markdown", + "id": "sec-small-scale", + "metadata": {}, + "source": [ + "## Small-scale example\n", + "\n", + "In this section, we benchmark the three compilation methods on Hamiltonian circuits with fewer than 20 qubits. These circuits transpile quickly and provide a clear view of how each method handles circuits of moderate complexity." + ] + }, + { + "cell_type": "markdown", + "id": "c4d5e6f7", + "metadata": {}, + "source": [ + "### Step 1: Map classical inputs to a quantum problem\n", + "\n", + "Each Hamiltonian is encoded as a `PauliEvolutionGate` circuit. The circuits were already constructed in the setup section from the Hamlib benchmark data." + ] + }, + { + "cell_type": "markdown", + "id": "d4e5f6a7", + "metadata": {}, + "source": [ + "### Step 2: Optimize problem for quantum hardware execution\n", + "\n", + "We transpile all small-scale circuits using each of the three pass managers and collect the metrics." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e4f5a6b7", + "metadata": {}, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "AI transpilation\n" + "[SABRE] Circuit 0 (all-vib-bh): 2Q depth=3, size=30, time=0.08s\n", + "[SABRE] Circuit 1 (all-vib-c2h): 2Q depth=18, size=114, time=0.01s\n", + "[SABRE] Circuit 2 (all-vib-o3): 2Q depth=6, size=58, time=0.00s\n", + "[SABRE] Circuit 3 (all-vib-c2h): 2Q depth=2, size=40, time=0.01s\n", + "[SABRE] Circuit 4 (graph-gnp_k-2): 2Q depth=24, size=130, time=0.01s\n", + "[SABRE] Circuit 5 (LiH): 2Q depth=66, size=289, time=0.01s\n", + "[SABRE] Circuit 6 (all-vib-fccf): 2Q depth=66, size=339, time=0.01s\n", + "[SABRE] Circuit 7 (all-vib-ch2): 2Q depth=88, size=416, time=0.01s\n", + "[SABRE] Circuit 8 (all-vib-f2): 2Q depth=180, size=1023, time=0.02s\n", + "[SABRE] Circuit 9 (all-vib-bhf2): 2Q depth=18, size=227, time=0.03s\n", + "[SABRE] Circuit 10 (graph-gnp_k-4): 2Q depth=122, size=695, time=0.01s\n", + "[SABRE] Circuit 11 (Be2): 2Q depth=343, size=1618, time=0.03s\n", + "[SABRE] Circuit 12 (all-vib-fccf): 2Q depth=14, size=134, time=0.01s\n", + "[SABRE] Circuit 13 (uf20-ham): 2Q depth=50, size=339, time=0.01s\n", + "[SABRE] Circuit 14 (TSP_Ncity-4): 2Q depth=118, size=615, time=0.01s\n", + "[SABRE] Circuit 15 (graph-complete_bipart): 2Q depth=232, size=1421, time=0.03s\n", + "[SABRE] Circuit 16 (all-vib-cyclo_propene): 2Q depth=18, size=359, time=1.01s\n", + "[SABRE] Circuit 17 (all-vib-hno): 2Q depth=6, size=174, time=0.14s\n", + "[SABRE] Circuit 18 (all-vib-fccf): 2Q depth=30, size=286, time=0.01s\n", + "[SABRE] Circuit 19 (tfim): 2Q depth=31, size=232, time=0.02s\n", + "[AI] Circuit 0 (all-vib-bh): 2Q depth=3, size=30, time=0.01s\n" ] }, { "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "b6a841c20fb34dbf84989243143f5f77", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "\"Output" + "Fetching 4 files: 0%| | 0/4 [00:00\n", - "\n", - "\n", + "
\n", + " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", + " \n", + " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Mean ± Std Dev per Compilation Method (% improvement relative to SABRE)
methodqc_nameqc_indexnum_qubitsopsdepthsizeruntime
0sabreSU2_10010{'rz': 81, 'sx': 70, 'cz': 16}131670.058845
1sabreSU2_20120{'rz': 160, 'sx': 119, 'cz': 20}202990.238217
2sabreSU2_30230{'sx': 295, 'rz': 242, 'cz': 90}7262710.723922
3sabreSU2_40340{'rz': 320, 'sx': 239, 'cz': 40}4059916.159262 2Q Depth% impr. (2Q Depth)Gate Count% impr. (Gate Count)Runtime (s)% impr. (Runtime (s))
4sabreSU2_50450{'rz': 402, 'sx': 367, 'cz': 86}7785576.886604
5sabreSU2_60560{'rz': 480, 'sx': 359, 'cz': 60}6089986.118255
6sabreSU2_70670{'rz': 562, 'sx': 441, 'cz': 82}79108594.458287
7sabreSU2_80780{'rz': 640, 'sx': 479, 'cz': 80}80119969.048184
8sabreSU2_90890{'rz': 721, 'sx': 585, 'cz': 114}105142088.254809
9sabreSU2_1009100{'rz': 800, 'sx': 599, 'cz': 100}100149983.795482
10aiSU2_10010{'rz': 81, 'sx': 71, 'cz': 16}101680.171532
11aiSU2_20120{'rz': 160, 'sx': 119, 'cz': 20}202990.291691Method      
12aiSU2_30230{'sx': 243, 'rz': 242, 'cz': 63}3654813.555931AI69.3 ± 80.8-10.0% ± 28.1%423.8 ± 427.3-4.4% ± 18.2%0.2 ± 0.2-1114.5% ± 1050.4%
13aiSU2_40340{'rz': 320, 'sx': 239, 'cz': 40}4059915.952733Rustiq67.9 ± 80.0-25.3% ± 85.4%453.9 ± 489.0-15.4% ± 50.5%0.0 ± 0.1+3.8% ± 50.1%
14aiSU2_50450{'rz': 403, 'sx': 346, 'cz': 74}5482380.702141SABRE71.8 ± 89.6+0.0% ± 0.0%426.9 ± 446.4+0.0% ± 0.0%0.1 ± 0.2+0.0% ± 0.0%
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "styled_summary_table(results_small)" + ] + }, + { + "cell_type": "markdown", + "id": "per_circuit_small_md", + "metadata": {}, + "source": [ + "The per-circuit table lets you see how each method compares on individual circuits. The best value for each metric is highlighted in green. Notice that for the simplest circuits, all three methods often converge to the same result." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "per_circuit_small_code", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - "
Per-Circuit Comparison (first 8 circuits by qubit count)
IndexCircuitQubits2Q Depth (SABRE)2Q Depth (AI)2Q Depth (Rustiq)Gate Count (SABRE)Gate Count (AI)Gate Count (Rustiq)Runtime (s) (SABRE)Runtime (s) (AI)Runtime (s) (Rustiq)
0all-vib-bh23.03.03.030.030.030.00.10.00.0
1all-vib-c2h318.018.013.0114.0110.074.00.00.20.0
2all-vib-o346.010.013.058.081.082.00.00.00.0
15aiSU2_60560{'rz': 480, 'sx': 359, 'cz': 60}6089975.9934043all-vib-c2h42.02.02.040.040.037.00.00.00.0
16aiSU2_70670{'rz': 563, 'sx': 442, 'cz': 82}74108764.9601624graph-gnp_k-2424.025.031.0130.0136.0132.00.00.10.0
17aiSU2_80780{'rz': 640, 'sx': 479, 'cz': 80}80119968.2532805LiH466.062.059.0289.0258.0286.00.00.20.0
18aiSU2_90890{'rz': 721, 'sx': 575, 'cz': 108}90140475.0724126all-vib-fccf466.065.034.0339.0301.0191.00.00.20.0
19aiSU2_1009100{'rz': 800, 'sx': 599, 'cz': 100}100149963.9674467all-vib-ch2488.081.049.0416.0379.0302.00.00.20.0
\n", - "" + "\n" ], "text/plain": [ - " method qc_name qc_index num_qubits ops \\\n", - "0 sabre SU2_10 0 10 {'rz': 81, 'sx': 70, 'cz': 16} \n", - "1 sabre SU2_20 1 20 {'rz': 160, 'sx': 119, 'cz': 20} \n", - "2 sabre SU2_30 2 30 {'sx': 295, 'rz': 242, 'cz': 90} \n", - "3 sabre SU2_40 3 40 {'rz': 320, 'sx': 239, 'cz': 40} \n", - "4 sabre SU2_50 4 50 {'rz': 402, 'sx': 367, 'cz': 86} \n", - "5 sabre SU2_60 5 60 {'rz': 480, 'sx': 359, 'cz': 60} \n", - "6 sabre SU2_70 6 70 {'rz': 562, 'sx': 441, 'cz': 82} \n", - "7 sabre SU2_80 7 80 {'rz': 640, 'sx': 479, 'cz': 80} \n", - "8 sabre SU2_90 8 90 {'rz': 721, 'sx': 585, 'cz': 114} \n", - "9 sabre SU2_100 9 100 {'rz': 800, 'sx': 599, 'cz': 100} \n", - "10 ai SU2_10 0 10 {'rz': 81, 'sx': 71, 'cz': 16} \n", - "11 ai SU2_20 1 20 {'rz': 160, 'sx': 119, 'cz': 20} \n", - "12 ai SU2_30 2 30 {'sx': 243, 'rz': 242, 'cz': 63} \n", - "13 ai SU2_40 3 40 {'rz': 320, 'sx': 239, 'cz': 40} \n", - "14 ai SU2_50 4 50 {'rz': 403, 'sx': 346, 'cz': 74} \n", - "15 ai SU2_60 5 60 {'rz': 480, 'sx': 359, 'cz': 60} \n", - "16 ai SU2_70 6 70 {'rz': 563, 'sx': 442, 'cz': 82} \n", - "17 ai SU2_80 7 80 {'rz': 640, 'sx': 479, 'cz': 80} \n", - "18 ai SU2_90 8 90 {'rz': 721, 'sx': 575, 'cz': 108} \n", - "19 ai SU2_100 9 100 {'rz': 800, 'sx': 599, 'cz': 100} \n", - "\n", - " depth size runtime \n", - "0 13 167 0.058845 \n", - "1 20 299 0.238217 \n", - "2 72 627 10.723922 \n", - "3 40 599 16.159262 \n", - "4 77 855 76.886604 \n", - "5 60 899 86.118255 \n", - "6 79 1085 94.458287 \n", - "7 80 1199 69.048184 \n", - "8 105 1420 88.254809 \n", - "9 100 1499 83.795482 \n", - "10 10 168 0.171532 \n", - "11 20 299 0.291691 \n", - "12 36 548 13.555931 \n", - "13 40 599 15.952733 \n", - "14 54 823 80.702141 \n", - "15 60 899 75.993404 \n", - "16 74 1087 64.960162 \n", - "17 80 1199 68.253280 \n", - "18 90 1404 75.072412 \n", - "19 100 1499 63.967446 " + "" ] }, - "execution_count": 10, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "summary_su2 = (\n", - " results_su2.groupby(\"method\")[[\"depth\", \"size\", \"runtime\"]]\n", - " .mean()\n", - " .round(2)\n", - ")\n", - "print(summary_su2)\n", - "\n", - "results_su2" + "per_circuit_comparison(results_small, num_rows=8)" ] }, { "cell_type": "markdown", - "id": "167834f1-00a7-4190-99e4-d221d1952357", + "id": "b5c6d7e8", "metadata": {}, "source": [ - "#### Results graph\n", + "#### Visualize results\n", "\n", - "As we define a function to consistently capture metrics, we will also define one to graph the metrics. Here, we will plot the two-qubit depth, gate count, and runtime for each compilation method across the circuits." + "The plots below compare the three methods across each metric on a per-circuit basis. Circuits are sorted by qubit count and labeled by index on the x-axis, since multiple circuits can share the same number of qubits." ] }, { "cell_type": "code", - "execution_count": 11, - "id": "d90fb4fa-e031-40f6-90e6-e1208a855bec", + "execution_count": 15, + "id": "c5d6e7f8", "metadata": {}, "outputs": [], "source": [ - "def plot_transpilation_metrics(results, overall_title, x_axis=\"qc_index\"):\n", + "def plot_transpilation_comparison(results, title_prefix):\n", " \"\"\"\n", - " Plots transpilation metrics (depth, size, runtime) for different transpilation methods.\n", + " Create a three-panel figure comparing compilation methods on\n", + " two-qubit depth, circuit size, and runtime.\n", "\n", - " Parameters:\n", - " results (DataFrame): Data containing columns ['num_qubits', 'method', 'depth', 'size', 'runtime']\n", - " overall_title (str): The title of the overall figure.\n", - " x_axis (str): The x-axis label, either 'num_qubits' or 'qc_index'.\n", + " Circuits are sorted by qubit count and plotted by circuit index.\n", " \"\"\"\n", - "\n", - " fig, axs = plt.subplots(1, 3, figsize=(24, 6))\n", - " metrics = [\"depth\", \"size\", \"runtime\"]\n", - " titles = [\"Circuit Depth\", \"Circuit Size\", \"Transpilation Runtime\"]\n", - " y_labels = [\"Depth\", \"Size (Gate Count)\", \"Runtime (s)\"]\n", + " # Sort circuits by num_qubits for consistent ordering\n", + " idx_order = (\n", + " results[results[\"method\"] == results[\"method\"].iloc[0]]\n", + " .sort_values(\"num_qubits\")[\"qc_index\"]\n", + " .values\n", + " )\n", + " pos_map = {orig: pos for pos, orig in enumerate(idx_order)}\n", + " results = results.copy()\n", + " results[\"plot_pos\"] = results[\"qc_index\"].map(pos_map)\n", "\n", " methods = results[\"method\"].unique()\n", - " colors = plt.colormaps[\"tab10\"]\n", - " markers = [\"o\", \"^\", \"s\", \"D\", \"P\", \"*\", \"X\", \"v\"]\n", - " color_list = [colors(i % colors.N) for i in range(len(methods))]\n", - " color_map = {method: color_list[i] for i, method in enumerate(methods)}\n", - " marker_map = {\n", - " method: markers[i % len(markers)] for i, method in enumerate(methods)\n", - " }\n", - " jitter_factor = 0.1 # Small x-axis jitter for visibility\n", - " handles, labels = [], [] # Unique handles for legend\n", + " palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n", + " markers = {\"SABRE\": \"o\", \"AI\": \"^\", \"Rustiq\": \"s\"}\n", "\n", - " # Plot each metric\n", - " for i, metric in enumerate(metrics):\n", - " for method in methods:\n", - " method_data = results[results[\"method\"] == method]\n", + " metrics = [\n", + " (\"two_qubit_depth\", \"Two-Qubit Depth\"),\n", + " (\"size\", \"Total Gate Count (Circuit Size)\"),\n", + " (\"runtime\", \"Transpilation Runtime (s)\"),\n", + " ]\n", "\n", - " # Introduce slight jitter to avoid exact overlap\n", - " jitter = np.random.uniform(\n", - " -jitter_factor, jitter_factor, len(method_data)\n", - " )\n", + " fig, axes = plt.subplots(1, 3, figsize=(20, 5.5))\n", + " fig.suptitle(title_prefix, fontsize=15, fontweight=\"bold\", y=1.02)\n", + "\n", + " # Build x-tick labels: \"idx\\n(Nq)\" on separate lines\n", + " ref = results[results[\"method\"] == methods[0]].sort_values(\"plot_pos\")\n", + " tick_positions = ref[\"plot_pos\"].values\n", + " tick_labels = [\n", + " f\"{int(row.plot_pos)} ({int(row.num_qubits)}q)\"\n", + " for _, row in ref.iterrows()\n", + " ]\n", "\n", - " scatter = axs[i].scatter(\n", - " method_data[x_axis] + jitter,\n", - " method_data[metric],\n", - " color=color_map[method],\n", + " for ax, (metric, ylabel) in zip(axes, metrics):\n", + " for method in methods:\n", + " subset = results[results[\"method\"] == method].sort_values(\n", + " \"plot_pos\"\n", + " )\n", + " ax.plot(\n", + " subset[\"plot_pos\"],\n", + " subset[metric],\n", + " marker=markers.get(method, \"o\"),\n", " label=method,\n", - " marker=marker_map[method],\n", - " alpha=0.7,\n", - " edgecolors=\"black\",\n", - " s=80,\n", + " color=palette.get(method, None),\n", + " linewidth=1.5,\n", + " markersize=6,\n", + " alpha=0.85,\n", " )\n", + " ax.set_xlabel(\"Circuit Index (num qubits)\", fontsize=11)\n", + " ax.set_ylabel(ylabel, fontsize=11)\n", + " ax.legend(frameon=True, fontsize=9)\n", + " ax.grid(True, linestyle=\"--\", alpha=0.4)\n", + " step = max(1, len(tick_positions) // 15)\n", + " ax.set_xticks(tick_positions[::step])\n", + " ax.set_xticklabels(\n", + " [tick_labels[i] for i in range(0, len(tick_labels), step)],\n", + " fontsize=7,\n", + " rotation=45,\n", + " ha=\"right\",\n", + " )\n", "\n", - " if method not in labels:\n", - " handles.append(scatter)\n", - " labels.append(method)\n", - "\n", - " axs[i].set_title(titles[i])\n", - " axs[i].set_xlabel(x_axis)\n", - " axs[i].set_ylabel(y_labels[i])\n", - " axs[i].grid(axis=\"y\", linestyle=\"--\", alpha=0.7)\n", - " axs[i].tick_params(axis=\"x\", rotation=45)\n", - " axs[i].set_xticks(sorted(results[x_axis].unique()))\n", - "\n", - " fig.suptitle(overall_title, fontsize=16)\n", - " fig.legend(\n", - " handles=handles,\n", - " labels=labels,\n", - " loc=\"upper right\",\n", - " bbox_to_anchor=(1.05, 1),\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "pct_improvement_plot_fn", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_pct_improvement_vs_sabre(results, title_prefix):\n", + " \"\"\"\n", + " Plot the per-circuit percent improvement of each non-SABRE method\n", + " relative to SABRE, for each metric. A positive value means the\n", + " method improved on SABRE; negative means SABRE was better.\n", + " \"\"\"\n", + " metrics = [\n", + " (\"two_qubit_depth\", \"2Q Depth\"),\n", + " (\"size\", \"Gate Count\"),\n", + " (\"runtime\", \"Runtime\"),\n", + " ]\n", + " palette = {\"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n", + " markers = {\"AI\": \"^\", \"Rustiq\": \"s\"}\n", + "\n", + " sabre = (\n", + " results[results[\"method\"] == \"SABRE\"]\n", + " .sort_values(\"num_qubits\")\n", + " .reset_index(drop=True)\n", + " )\n", + " methods = [m for m in results[\"method\"].unique() if m != \"SABRE\"]\n", + "\n", + " fig, axes = plt.subplots(1, 3, figsize=(20, 5.5))\n", + " fig.suptitle(\n", + " f\"{title_prefix}: % Improvement over SABRE\",\n", + " fontsize=15,\n", + " fontweight=\"bold\",\n", + " y=1.02,\n", " )\n", "\n", + " tick_positions = sabre.index.values\n", + " tick_labels = [\n", + " f\"{i} ({int(row.num_qubits)}q)\"\n", + " for i, (_, row) in enumerate(sabre.iterrows())\n", + " ]\n", + "\n", + " for ax, (metric, label) in zip(axes, metrics):\n", + " ax.axhline(0, color=\"#1f77b4\", linewidth=2, label=\"SABRE (baseline)\")\n", + " for method in methods:\n", + " method_data = (\n", + " results[results[\"method\"] == method]\n", + " .sort_values(\"num_qubits\")\n", + " .reset_index(drop=True)\n", + " )\n", + " pct = (sabre[metric] - method_data[metric]) / sabre[metric] * 100\n", + " ax.plot(\n", + " tick_positions,\n", + " pct,\n", + " marker=markers.get(method, \"o\"),\n", + " label=method,\n", + " color=palette.get(method, None),\n", + " linewidth=1.5,\n", + " markersize=6,\n", + " alpha=0.85,\n", + " )\n", + " ax.set_xlabel(\"Circuit Index (num qubits)\", fontsize=11)\n", + " ax.set_ylabel(f\"% Improvement ({label})\", fontsize=11)\n", + " ax.legend(frameon=True, fontsize=9)\n", + " ax.grid(True, linestyle=\"--\", alpha=0.4)\n", + " step = max(1, len(tick_positions) // 15)\n", + " ax.set_xticks(tick_positions[::step])\n", + " ax.set_xticklabels(\n", + " [tick_labels[i] for i in range(0, len(tick_labels), step)],\n", + " fontsize=7,\n", + " rotation=45,\n", + " ha=\"right\",\n", + " )\n", + " ylims = ax.get_ylim()\n", + " ax.axhspan(0, max(ylims[1], 1), alpha=0.04, color=\"green\")\n", + " ax.axhspan(min(ylims[0], -1), 0, alpha=0.04, color=\"red\")\n", + "\n", " plt.tight_layout()\n", " plt.show()" ] }, { "cell_type": "code", - "execution_count": 12, - "id": "7f7b502a-8ed6-45fa-a698-02977149e283", + "execution_count": 17, + "id": "d5e6f7a8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, "metadata": {}, @@ -905,521 +1224,862 @@ } ], "source": [ - "plot_transpilation_metrics(\n", - " results_su2, \"Transpilation Metrics for SU2 Circuits\", x_axis=\"num_qubits\"\n", + "plot_transpilation_comparison(\n", + " results_small,\n", + " \"Small-Scale Hamiltonian Circuits: Compilation Comparison\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "pct_improvement_small", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_pct_improvement_vs_sabre(\n", + " results_small,\n", + " \"Small-Scale Hamiltonian Circuits\",\n", ")" ] }, { "cell_type": "markdown", - "id": "f75e8e3a-803f-4386-a60a-b088faa3c81a", + "id": "d2a29d04", "metadata": {}, "source": [ - "#### Analysis of SU2 circuit compilation results\n", + "At this scale, all three pass managers perform well, and their average results are close to each other. This is largely because small circuits leave limited room for further optimization, so the methods tend to converge on similar solutions.\n", "\n", - "In this experiment, we compare two transpilation methods — Qiskit's SABRE transpiler and the AI-powered transpiler — on a set of `efficient_su2` circuits. Since these circuits do not include any `PauliEvolutionGate` operations, the Rustiq plugin is not included in this comparison.\n", + "That said, there are notable differences in consistency. Rustiq produces the most variable results, with the largest outliers in both two-qubit depth and gate count. While this variability means it sometimes falls behind, it also means Rustiq occasionally finds significantly better solutions than the other two methods. The AI transpiler, on the other hand, is more stable in its results relative to SABRE, tracking closely on most circuits without large swings in either direction.\n", "\n", - "On average, the AI transpiler performs better in terms of circuit depth, with a greater than 10% improvement across the full range of SU2 circuits. For gate count (circuit size) and transpilation runtime, both methods yield similar results overall.\n", - "\n", - "However, inspecting the individual data points reveals a deeper insight:\n", - "- For most qubit sizes, both SABRE and AI produce nearly identical results, suggesting that in many cases, both methods converge to similarly efficient solutions.\n", - "- For certain circuit sizes, specifically at 30, 50, 70, and 90 qubits, the AI transpiler finds significantly shallower circuits than SABRE. This indicates that AI's learning-based approach is able to discover more optimal layouts or routing paths in cases where the SABRE heuristic does not.\n", - "\n", - "This behavior highlights an important takeaway:\n", - "> While SABRE and AI often produce comparable results, the AI transpiler can occasionally discover much better solutions, particularly in terms of depth, which can lead to significantly improved performance on hardware." + "For runtime, SABRE and Rustiq are both fast, while the AI transpiler is noticeably slower on certain circuits." ] }, { "cell_type": "markdown", - "id": "4c3b2aa8-8187-488a-8e5b-197cf26085bb", + "id": "e5f6a7b8", "metadata": {}, "source": [ - "## Part 2: Hamiltonian simulation circuit\n", + "#### Best-performing method by metric\n", "\n", - "### Step 1: Investigate circuits with `PauliEvolutionGate`\n", + "The chart below shows how often each method achieved the best (lowest) value for each metric. Ties are possible: for simpler circuits, multiple methods can reach the same optimal two-qubit depth or gate count. When a tie occurs, all tied methods receive credit, so the percentages for a given metric may sum to more than 100%." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f5a6b7c8", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_best_method_bars(results, metrics_list=None):\n", + " \"\"\"\n", + " Plot a grouped bar chart showing the percentage of circuits\n", + " where each method achieved the best (lowest) value for each metric.\n", "\n", - "In this section, we investigate quantum circuits constructed using `PauliEvolutionGate`, which enables efficient simulation of Hamiltonians. We will analyze how different compilation methods optimize these circuits across various Hamiltonians.\n", + " Ties are counted for all tied methods, so percentages per metric\n", + " can sum to more than 100%.\n", + " \"\"\"\n", + " if metrics_list is None:\n", + " metrics_list = [\"two_qubit_depth\", \"size\", \"runtime\"]\n", "\n", - "#### Hamiltonians used in the benchmark\n", + " methods = results[\"method\"].unique()\n", + " palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n", + " n_circuits = results[\"qc_index\"].nunique()\n", + "\n", + " win_data = {m: [] for m in methods}\n", + " tie_counts = [] # number of circuits with ties, per metric\n", + " metric_labels = []\n", + "\n", + " for metric in metrics_list:\n", + " if metric == \"two_qubit_depth\":\n", + " label = \"2Q Depth\"\n", + " elif metric == \"size\":\n", + " label = \"Gate Count\"\n", + " elif metric == \"runtime\":\n", + " label = \"Runtime\"\n", + " else:\n", + " label = metric.replace(\"_\", \" \").title()\n", + " metric_labels.append(label)\n", + "\n", + " counts = Counter()\n", + " ties = 0\n", + " for _, group in results.groupby(\"qc_index\"):\n", + " min_val = group[metric].min()\n", + " best = group[group[metric] == min_val][\"method\"]\n", + " if len(best) > 1:\n", + " ties += 1\n", + " counts.update(best)\n", + "\n", + " tie_counts.append(ties)\n", + " for m in methods:\n", + " win_data[m].append(counts.get(m, 0) / n_circuits * 100)\n", + "\n", + " x = np.arange(len(metric_labels))\n", + " width = 0.22\n", + " fig, ax = plt.subplots(figsize=(8, 5))\n", + "\n", + " for i, method in enumerate(methods):\n", + " bars = ax.bar(\n", + " x + i * width,\n", + " win_data[method],\n", + " width,\n", + " label=method,\n", + " color=palette.get(method, None),\n", + " edgecolor=\"black\",\n", + " linewidth=0.5,\n", + " )\n", + " for bar in bars:\n", + " height = bar.get_height()\n", + " if height > 0:\n", + " ax.text(\n", + " bar.get_x() + bar.get_width() / 2,\n", + " height + 1.5,\n", + " f\"{height:.0f}%\",\n", + " ha=\"center\",\n", + " va=\"bottom\",\n", + " fontsize=9,\n", + " )\n", + "\n", + " # Annotate tie counts below each metric label\n", + " for j, ties in enumerate(tie_counts):\n", + " if ties > 0:\n", + " ax.text(\n", + " x[j] + width,\n", + " -8,\n", + " f\"({ties} tie{\"s\" if ties != 1 else \"\"})\",\n", + " ha=\"center\",\n", + " va=\"top\",\n", + " fontsize=8,\n", + " color=\"gray\",\n", + " )\n", "\n", - "The Hamiltonians used in this benchmark describe pairwise interactions between qubits, including terms such as $ZZ$, $XX$, and $YY$. These Hamiltonians are commonly used in quantum chemistry, condensed matter physics, and materials science, where they model systems of interacting particles.\n", + " ax.set_xticks(x + width)\n", + " ax.set_xticklabels(metric_labels, fontsize=11)\n", + " ax.set_ylabel(\"Circuits with best value (%)\", fontsize=11)\n", + " ax.set_title(\n", + " \"Best-Performing Method by Metric (ties counted for all tied methods)\",\n", + " fontsize=12,\n", + " fontweight=\"bold\",\n", + " )\n", + " ax.legend(frameon=True, fontsize=10)\n", + " ax.set_ylim(-12, 120)\n", + " ax.yaxis.set_major_formatter(ticker.PercentFormatter())\n", + " ax.grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n", "\n", - "For reference, users can explore a broader set of Hamiltonians in this paper: [Efficient Hamiltonian Simulation on Noisy Quantum Devices](https://arxiv.org/pdf/2306.13126).\n", + " plt.tight_layout()\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a6b7c8d9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot_best_method_bars(results_small)" + ] + }, + { + "cell_type": "markdown", + "id": "30687d7b", + "metadata": {}, + "source": [ + "Rustiq wins on the majority of small-scale circuits across all three metrics. This is expected, as Rustiq is specifically designed for synthesizing `PauliEvolutionGate` operations like those in these Hamlib circuits. SABRE remains competitive, winning or tying on a significant share of circuits, while the AI transpiler picks up wins on select cases. The number of ties reflects the fact that for the simplest circuits, there is often only one optimal solution and multiple methods find it." + ] + }, + { + "cell_type": "markdown", + "id": "b6c7d8e9", + "metadata": {}, + "source": [ + "### Step 3: Execute using Qiskit primitives\n", "\n", - "#### Benchmark source: Hamlib and Benchpress\n", + "To evaluate how transpilation quality affects execution under noise, we use a **mirror circuit** technique. For each transpiled circuit $U$, we append its inverse $U^\\dagger$ so the combined circuit $U^\\dagger U$ is theoretically the identity. Starting from the $|0\\rangle$ state, a perfect (noiseless) execution would return the all-zeros bitstring with probability 1.\n", "\n", - "The circuits used in this benchmark are drawn from the [Hamlib benchmark repository](https://github.com/SRI-International/QC-App-Oriented-Benchmarks/tree/master/hamlib), which contains realistic Hamiltonian simulation workloads.\n", + "In practice, gate errors accumulate throughout the circuit, so the probability of recovering $|0\\rangle^{\\otimes n}$ (the **survival probability**) drops. A compilation method that produces a shallower circuit with fewer gates will accumulate less noise and achieve a higher survival probability.\n", "\n", - "These same circuits were previously benchmarked using [Benchpress](https://github.com/Qiskit/benchpress), an open-source framework for evaluating quantum transpilation performance. By using this standardized set of circuits, we can directly compare the effectiveness of different compilation strategies on representative simulation problems.\n", + "The mirror circuit approach is appealingly simple and scales to any circuit size, since the expected output is always $|0\\rangle^{\\otimes n}$ and no classical simulation of the ideal state is required. However, there are caveats worth noting: the mirror circuit is a proxy for the actual circuit (not the circuit itself), it doubles the gate count (which exaggerates the effect of noise), and it can underestimate certain errors when noise cancels symmetrically across the mirror boundary.\n", "\n", - "Hamiltonian simulation is a foundational task in quantum computing, with applications in molecular simulations, optimization problems, and quantum many-body physics. Understanding how different compilation methods optimize these circuits can help users improve practical execution of such circuits on near-term quantum devices." + "We pick circuit index 6 from the small-scale set and run the mirror circuits on an Aer simulator with a simple depolarizing noise model." ] }, { "cell_type": "code", - "execution_count": 13, - "id": "66347c00-1607-4405-bb76-610690adf6b8", + "execution_count": 21, + "id": "c6d7e8f9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Number of Hamiltonian circuits: 35\n" + "Test circuit: all-vib-fccf, 4 qubits\n", + "\n", + "Transpilation metrics for circuit index 6:\n", + " SABRE 2Q depth= 66 size= 339\n", + " AI 2Q depth= 65 size= 301\n", + " Rustiq 2Q depth= 34 size= 191\n" ] - }, + } + ], + "source": [ + "# Select circuit index 6 from the small-scale transpiled circuits\n", + "test_idx = 6\n", + "test_circuit = qc_small[test_idx]\n", + "print(f\"Test circuit: {test_circuit.name}, {test_circuit.num_qubits} qubits\")\n", + "\n", + "# Get the transpiled versions\n", + "tqc_methods_small = {\n", + " \"SABRE\": tqc_sabre_small[test_idx],\n", + " \"AI\": tqc_ai_small[test_idx],\n", + " \"Rustiq\": tqc_rustiq_small[test_idx],\n", + "}\n", + "\n", + "# Show transpilation metrics for this circuit\n", + "print(f\"\\nTranspilation metrics for circuit index {test_idx}:\")\n", + "for method, tqc in tqc_methods_small.items():\n", + " depth_2q = tqc.depth(lambda x: x.operation.num_qubits == 2)\n", + " size = tqc.size()\n", + " print(f\" {method:8s} 2Q depth={depth_2q:5d} size={size:6d}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d6e7f8a9", + "metadata": {}, + "source": [ + "Build the mirror circuits (append $U^\\dagger$), remap to contiguous qubit indices so the simulator only handles the active qubits, and run on a noisy Aer simulator." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e6f7a8b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SABRE P(|00...0>) = 0.7889 (7889/10000)\n", + "AI P(|00...0>) = 0.8008 (8008/10000)\n", + "Rustiq P(|00...0>) = 0.8896 (8896/10000)\n" + ] + } + ], + "source": [ + "def remap_to_contiguous(tqc):\n", + " \"\"\"Remap a transpiled circuit to use contiguous qubit indices.\n", + "\n", + " Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)\n", + " on a large backend. This remaps them to 0, 1, 2, ... so Aer only\n", + " simulates the active qubits.\n", + " \"\"\"\n", + " active = sorted(\n", + " {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}\n", + " )\n", + " qubit_map = {old: new for new, old in enumerate(active)}\n", + " new_qc = QuantumCircuit(len(active))\n", + " for inst in tqc.data:\n", + " old_indices = [tqc.find_bit(q).index for q in inst.qubits]\n", + " new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])\n", + " return new_qc\n", + "\n", + "\n", + "def build_mirror_circuit(tqc):\n", + " \"\"\"Build a mirror circuit: U followed by U-dagger, with measurements.\n", + "\n", + " The combined circuit U-dagger @ U should be the identity, so measuring\n", + " all zeros indicates a noise-free execution.\n", + " \"\"\"\n", + " tqc_compact = remap_to_contiguous(tqc)\n", + " mirror = tqc_compact.compose(tqc_compact.inverse())\n", + " mirror.measure_all()\n", + " return mirror\n", + "\n", + "\n", + "# Build a simple depolarizing noise model\n", + "noise_model = NoiseModel()\n", + "noise_model.add_all_qubit_quantum_error(\n", + " depolarizing_error(0.001, 1),\n", + " [\"sx\", \"x\", \"rz\"], # ~0.1% per 1Q gate\n", + ")\n", + "noise_model.add_all_qubit_quantum_error(\n", + " depolarizing_error(0.01, 2),\n", + " [\"cx\", \"ecr\"], # ~1% per 2Q gate\n", + ")\n", + "\n", + "aer_sim = AerSimulator(noise_model=noise_model)\n", + "\n", + "shots = 10000\n", + "survival_probs = {}\n", + "\n", + "for method, tqc in tqc_methods_small.items():\n", + " mirror = build_mirror_circuit(tqc)\n", + "\n", + " sampler = SamplerV2(mode=aer_sim)\n", + " job = sampler.run([mirror], shots=shots)\n", + " result = job.result()\n", + " counts = result[0].data.meas.get_counts()\n", + "\n", + " # Survival probability = fraction of all-zeros outcomes\n", + " n_qubits = mirror.num_qubits - mirror.num_clbits # active qubits\n", + " all_zeros = \"0\" * mirror.num_qubits\n", + " survival = counts.get(all_zeros, 0) / shots\n", + " survival_probs[method] = survival\n", + " print(\n", + " f\"{method:8s} P(|00...0>) = {survival:.4f} ({counts.get(all_zeros, 0)}/{shots})\"\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "small_step4_plot", + "metadata": {}, + "outputs": [ { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, - "execution_count": 13, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "# Obtain the Hamiltonian JSON from the benchpress repository\n", - "url = \"https://raw.githubusercontent.com/Qiskit/benchpress/e7b29ef7be4cc0d70237b8fdc03edbd698908eff/benchpress/hamiltonian/hamlib/100_representative.json\"\n", - "response = requests.get(url)\n", - "response.raise_for_status() # Raise an error if download failed\n", - "ham_records = json.loads(response.text)\n", - "# Remove circuits that are too large for the backend\n", - "ham_records = [\n", - " h for h in ham_records if h[\"ham_qubits\"] <= backend.num_qubits\n", - "]\n", - "# Remove the circuits that are large to save transpilation time\n", - "ham_records = sorted(ham_records, key=lambda x: x[\"ham_terms\"])[:35]\n", + "def plot_mirror_results(tqc_methods, survival_probs, circuit_name):\n", + " \"\"\"\n", + " Plot a three-panel comparison: survival probability, 2Q depth,\n", + " and gate count for each compilation method.\n", + " \"\"\"\n", + " methods = list(tqc_methods.keys())\n", + " palette = {\"SABRE\": \"#1f77b4\", \"AI\": \"#ff7f0e\", \"Rustiq\": \"#2ca02c\"}\n", + " colors = [palette.get(m, \"gray\") for m in methods]\n", + "\n", + " surv_vals = [survival_probs[m] for m in methods]\n", + " depth_vals = [\n", + " tqc_methods[m].depth(lambda x: x.operation.num_qubits == 2)\n", + " for m in methods\n", + " ]\n", + " size_vals = [tqc_methods[m].size() for m in methods]\n", "\n", - "qc_ham_list = []\n", - "for h in ham_records:\n", - " terms = h[\"ham_hamlib_hamiltonian_terms\"]\n", - " coeff = h[\"ham_hamlib_hamiltonian_coefficients\"]\n", - " num_qubits = h[\"ham_qubits\"]\n", - " name = h[\"ham_problem\"]\n", + " fig, axes = plt.subplots(1, 3, figsize=(16, 5))\n", + " fig.suptitle(\n", + " f\"Mirror Circuit Results: {circuit_name}\",\n", + " fontsize=14,\n", + " fontweight=\"bold\",\n", + " y=1.02,\n", + " )\n", "\n", - " evo_gate = PauliEvolutionGate(SparsePauliOp(terms, coeff))\n", + " def _annotate_bars(ax, bars, values, fmt=\"{}\"):\n", + " ymax = ax.get_ylim()[1]\n", + " for bar, val in zip(bars, values):\n", + " label = fmt.format(val)\n", + " y = val + ymax * 0.03\n", + " ax.text(\n", + " bar.get_x() + bar.get_width() / 2,\n", + " y,\n", + " label,\n", + " ha=\"center\",\n", + " va=\"bottom\",\n", + " fontsize=10,\n", + " fontweight=\"bold\",\n", + " )\n", "\n", - " qc_ham = QuantumCircuit(num_qubits)\n", - " qc_ham.name = name\n", + " # Panel 1: Survival Probability\n", + " bars = axes[0].bar(\n", + " methods, surv_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n", + " )\n", + " axes[0].set_ylabel(\"Survival Probability P(|00...0>)\", fontsize=11)\n", + " axes[0].set_title(\"Survival Probability (higher is better)\", fontsize=12)\n", + " axes[0].set_ylim(0, max(surv_vals) * 1.18 if max(surv_vals) > 0 else 1.0)\n", + " axes[0].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n", + " _annotate_bars(axes[0], bars, surv_vals, fmt=\"{:.4f}\")\n", + "\n", + " # Panel 2: Two-Qubit Depth\n", + " bars = axes[1].bar(\n", + " methods, depth_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n", + " )\n", + " axes[1].set_ylabel(\"Two-Qubit Depth\", fontsize=11)\n", + " axes[1].set_title(\"2Q Depth (lower is better)\", fontsize=12)\n", + " axes[1].set_ylim(0, max(depth_vals) * 1.18)\n", + " axes[1].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n", + " _annotate_bars(axes[1], bars, depth_vals)\n", + "\n", + " # Panel 3: Gate Count\n", + " bars = axes[2].bar(\n", + " methods, size_vals, color=colors, edgecolor=\"black\", linewidth=0.5\n", + " )\n", + " axes[2].set_ylabel(\"Total Gate Count\", fontsize=11)\n", + " axes[2].set_title(\"Gate Count (lower is better)\", fontsize=12)\n", + " axes[2].set_ylim(0, max(size_vals) * 1.18)\n", + " axes[2].grid(axis=\"y\", linestyle=\"--\", alpha=0.4)\n", + " _annotate_bars(axes[2], bars, size_vals)\n", + "\n", + " plt.tight_layout()\n", + " plt.show()\n", "\n", - " qc_ham.append(evo_gate, range(num_qubits))\n", - " qc_ham_list.append(qc_ham)\n", - "print(f\"Number of Hamiltonian circuits: {len(qc_ham_list)}\")\n", "\n", - "# Draw the first Hamiltonian circuit\n", - "qc_ham_list[0].draw(\"mpl\", fold=-1)" + "plot_mirror_results(tqc_methods_small, survival_probs, test_circuit.name)" ] }, { "cell_type": "markdown", - "id": "9690d94d-7d38-45d3-b37d-85eaf268dbd6", + "id": "small_mirror_commentary", "metadata": {}, "source": [ - "### Step 2: Optimize problem for quantum hardware execution\n", + "#### Observations\n", "\n", - "As in the previous example, we will use the same backend to ensure consistency in our comparisons. Since the pass managers (`pm_sabre`, `pm_ai`, and `pm_rustiq`) have already been initialized, we can directly proceed with transpiling the Hamiltonian circuits using each method.\n", + "The method with the lowest two-qubit depth and fewest gates achieves the highest survival probability, consistent with the expectation that shorter circuits accumulate less noise. Even modest differences in depth and gate count translate into measurable differences in survival probability under the depolarizing noise model.\n", "\n", - "This step focuses solely on performing the transpilation and recording the resulting circuit metrics, including depth, gate count, and transpilation runtime. By analyzing these results, we aim to determine the efficiency of each transpilation method for this type of circuit." + "Keep in mind that these results are for a single circuit. The relative ranking of the methods can shift from circuit to circuit depending on the Hamiltonian structure. The key takeaway is the trend: lower depth and gate count generally lead to better output quality under noise." ] }, { "cell_type": "markdown", - "id": "22fb1fe7-333f-421f-a17d-bc32190a0f86", + "id": "sec-large-scale", "metadata": {}, "source": [ - "Transpile and capture metrics:" + "## Large-scale hardware example\n", + "\n", + "In this section, we benchmark the same three compilation methods on Hamiltonian circuits with 20 or more qubits. These circuits are more representative of practical Hamiltonian simulation workloads and test how each method scales in terms of circuit quality and compilation time." + ] + }, + { + "cell_type": "markdown", + "id": "f6a7b8c9", + "metadata": {}, + "source": [ + "### Steps 1-4 combined\n", + "\n", + "The workflow follows the same structure as the small-scale example. We transpile all large-scale circuits with each method, collect metrics, and then submit a mirror circuit to real quantum hardware." ] }, { "cell_type": "code", - "execution_count": 14, - "id": "4138c7d6-5ec8-4c8f-aef4-08ec1b13633b", + "execution_count": 24, + "id": "a7b8c9d0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method sabre, depth 6, and size 58.\n", - "Transpiled circuit index 1 (all-vib-c2h) in 1.10 seconds with method sabre, depth 2, and size 39.\n", - "Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method sabre, depth 3, and size 30.\n", - "Transpiled circuit index 3 (all-vib-c2h) in 0.03 seconds with method sabre, depth 18, and size 115.\n", - "Transpiled circuit index 4 (graph-gnp_k-2) in 0.02 seconds with method sabre, depth 24, and size 129.\n", - "Transpiled circuit index 5 (all-vib-fccf) in 0.05 seconds with method sabre, depth 14, and size 134.\n", - "Transpiled circuit index 6 (all-vib-hno) in 8.39 seconds with method sabre, depth 6, and size 174.\n", - "Transpiled circuit index 7 (all-vib-bhf2) in 3.92 seconds with method sabre, depth 22, and size 220.\n", - "Transpiled circuit index 8 (LiH) in 0.03 seconds with method sabre, depth 67, and size 290.\n", - "Transpiled circuit index 9 (uf20-ham) in 0.04 seconds with method sabre, depth 50, and size 340.\n", - "Transpiled circuit index 10 (all-vib-fccf) in 0.62 seconds with method sabre, depth 30, and size 286.\n", - "Transpiled circuit index 11 (all-vib-fccf) in 0.04 seconds with method sabre, depth 67, and size 339.\n", - "Transpiled circuit index 12 (all-vib-ch2) in 0.04 seconds with method sabre, depth 87, and size 421.\n", - "Transpiled circuit index 13 (tfim) in 0.05 seconds with method sabre, depth 36, and size 222.\n", - "Transpiled circuit index 14 (all-vib-cyclo_propene) in 9.51 seconds with method sabre, depth 22, and size 345.\n", - "Transpiled circuit index 15 (graph-gnp_k-4) in 0.05 seconds with method sabre, depth 128, and size 704.\n", - "Transpiled circuit index 16 (all-vib-hc3h2cn) in 13.83 seconds with method sabre, depth 2, and size 242.\n", - "Transpiled circuit index 17 (TSP_Ncity-4) in 0.05 seconds with method sabre, depth 106, and size 609.\n", - "Transpiled circuit index 18 (tfim) in 0.29 seconds with method sabre, depth 73, and size 399.\n", - "Transpiled circuit index 19 (all-vib-h2co) in 21.97 seconds with method sabre, depth 30, and size 572.\n", - "Transpiled circuit index 20 (Be2) in 0.09 seconds with method sabre, depth 324, and size 1555.\n", - "Transpiled circuit index 21 (graph-complete_bipart) in 0.12 seconds with method sabre, depth 250, and size 1394.\n", - "Transpiled circuit index 22 (all-vib-f2) in 0.07 seconds with method sabre, depth 215, and size 1027.\n", - "Transpiled circuit index 23 (all-vib-cyclo_propene) in 41.22 seconds with method sabre, depth 30, and size 1144.\n", - "Transpiled circuit index 24 (TSP_Ncity-5) in 1.89 seconds with method sabre, depth 175, and size 1933.\n", - "Transpiled circuit index 25 (H2) in 0.32 seconds with method sabre, depth 1237, and size 5502.\n", - "Transpiled circuit index 26 (uuf100-ham) in 0.20 seconds with method sabre, depth 385, and size 4303.\n", - "Transpiled circuit index 27 (ham-graph-gnp_k-5) in 0.20 seconds with method sabre, depth 311, and size 3654.\n", - "Transpiled circuit index 28 (tfim) in 0.15 seconds with method sabre, depth 276, and size 3213.\n", - "Transpiled circuit index 29 (uuf100-ham) in 0.21 seconds with method sabre, depth 520, and size 5250.\n", - "Transpiled circuit index 30 (flat100-ham) in 0.15 seconds with method sabre, depth 131, and size 3157.\n", - "Transpiled circuit index 31 (uf100-ham) in 0.24 seconds with method sabre, depth 624, and size 7378.\n", - "Transpiled circuit index 32 (OH) in 0.88 seconds with method sabre, depth 2175, and size 9808.\n", - "Transpiled circuit index 33 (HF) in 0.66 seconds with method sabre, depth 2206, and size 9417.\n", - "Transpiled circuit index 34 (BH) in 0.89 seconds with method sabre, depth 2177, and size 9802.\n", - "Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method ai, depth 6, and size 58.\n", - "Transpiled circuit index 1 (all-vib-c2h) in 1.11 seconds with method ai, depth 2, and size 39.\n", - "Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method ai, depth 3, and size 30.\n", - "Transpiled circuit index 3 (all-vib-c2h) in 0.11 seconds with method ai, depth 18, and size 94.\n", - "Transpiled circuit index 4 (graph-gnp_k-2) in 0.11 seconds with method ai, depth 22, and size 129.\n", - "Transpiled circuit index 5 (all-vib-fccf) in 0.06 seconds with method ai, depth 22, and size 177.\n", - "Transpiled circuit index 6 (all-vib-hno) in 8.62 seconds with method ai, depth 10, and size 198.\n", - "Transpiled circuit index 7 (all-vib-bhf2) in 3.71 seconds with method ai, depth 18, and size 195.\n", - "Transpiled circuit index 8 (LiH) in 0.19 seconds with method ai, depth 62, and size 267.\n", - "Transpiled circuit index 9 (uf20-ham) in 0.22 seconds with method ai, depth 47, and size 321.\n", - "Transpiled circuit index 10 (all-vib-fccf) in 0.71 seconds with method ai, depth 38, and size 369.\n", - "Transpiled circuit index 11 (all-vib-fccf) in 0.24 seconds with method ai, depth 65, and size 315.\n", - "Transpiled circuit index 12 (all-vib-ch2) in 0.24 seconds with method ai, depth 91, and size 430.\n", - "Transpiled circuit index 13 (tfim) in 0.15 seconds with method ai, depth 12, and size 251.\n", - "Transpiled circuit index 14 (all-vib-cyclo_propene) in 8.50 seconds with method ai, depth 18, and size 311.\n", - "Transpiled circuit index 15 (graph-gnp_k-4) in 0.25 seconds with method ai, depth 117, and size 659.\n", - "Transpiled circuit index 16 (all-vib-hc3h2cn) in 16.11 seconds with method ai, depth 2, and size 242.\n", - "Transpiled circuit index 17 (TSP_Ncity-4) in 0.39 seconds with method ai, depth 98, and size 564.\n", - "Transpiled circuit index 18 (tfim) in 0.38 seconds with method ai, depth 23, and size 437.\n", - "Transpiled circuit index 19 (all-vib-h2co) in 24.97 seconds with method ai, depth 38, and size 707.\n", - "Transpiled circuit index 20 (Be2) in 1.07 seconds with method ai, depth 293, and size 1392.\n", - "Transpiled circuit index 21 (graph-complete_bipart) in 0.61 seconds with method ai, depth 229, and size 1437.\n", - "Transpiled circuit index 22 (all-vib-f2) in 0.57 seconds with method ai, depth 178, and size 964.\n", - "Transpiled circuit index 23 (all-vib-cyclo_propene) in 50.89 seconds with method ai, depth 34, and size 1425.\n", - "Transpiled circuit index 24 (TSP_Ncity-5) in 1.61 seconds with method ai, depth 171, and size 2020.\n", - "Transpiled circuit index 25 (H2) in 6.39 seconds with method ai, depth 1148, and size 5208.\n", - "Transpiled circuit index 26 (uuf100-ham) in 3.97 seconds with method ai, depth 376, and size 5048.\n", - "Transpiled circuit index 27 (ham-graph-gnp_k-5) in 3.54 seconds with method ai, depth 357, and size 4451.\n", - "Transpiled circuit index 28 (tfim) in 1.72 seconds with method ai, depth 216, and size 3026.\n", - "Transpiled circuit index 29 (uuf100-ham) in 4.45 seconds with method ai, depth 426, and size 5399.\n", - "Transpiled circuit index 30 (flat100-ham) in 7.02 seconds with method ai, depth 86, and size 3108.\n", - "Transpiled circuit index 31 (uf100-ham) in 12.85 seconds with method ai, depth 623, and size 8354.\n", - "Transpiled circuit index 32 (OH) in 15.19 seconds with method ai, depth 2084, and size 9543.\n", - "Transpiled circuit index 33 (HF) in 17.51 seconds with method ai, depth 2063, and size 9446.\n", - "Transpiled circuit index 34 (BH) in 15.33 seconds with method ai, depth 2094, and size 9730.\n", - "Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method rustiq, depth 13, and size 83.\n", - "Transpiled circuit index 1 (all-vib-c2h) in 1.11 seconds with method rustiq, depth 2, and size 39.\n", - "Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method rustiq, depth 3, and size 30.\n", - "Transpiled circuit index 3 (all-vib-c2h) in 0.01 seconds with method rustiq, depth 13, and size 79.\n", - "Transpiled circuit index 4 (graph-gnp_k-2) in 0.02 seconds with method rustiq, depth 31, and size 131.\n", - "Transpiled circuit index 5 (all-vib-fccf) in 0.04 seconds with method rustiq, depth 50, and size 306.\n", - "Transpiled circuit index 6 (all-vib-hno) in 14.03 seconds with method rustiq, depth 22, and size 276.\n", - "Transpiled circuit index 7 (all-vib-bhf2) in 3.15 seconds with method rustiq, depth 13, and size 155.\n", - "Transpiled circuit index 8 (LiH) in 0.03 seconds with method rustiq, depth 54, and size 270.\n", - "Transpiled circuit index 9 (uf20-ham) in 0.04 seconds with method rustiq, depth 65, and size 398.\n", - "Transpiled circuit index 10 (all-vib-fccf) in 0.16 seconds with method rustiq, depth 41, and size 516.\n", - "Transpiled circuit index 11 (all-vib-fccf) in 0.02 seconds with method rustiq, depth 34, and size 189.\n", - "Transpiled circuit index 12 (all-vib-ch2) in 0.03 seconds with method rustiq, depth 49, and size 240.\n", - "Transpiled circuit index 13 (tfim) in 0.05 seconds with method rustiq, depth 20, and size 366.\n", - "Transpiled circuit index 14 (all-vib-cyclo_propene) in 9.08 seconds with method rustiq, depth 16, and size 277.\n", - "Transpiled circuit index 15 (graph-gnp_k-4) in 0.04 seconds with method rustiq, depth 116, and size 612.\n", - "Transpiled circuit index 16 (all-vib-hc3h2cn) in 13.89 seconds with method rustiq, depth 2, and size 257.\n", - "Transpiled circuit index 17 (TSP_Ncity-4) in 0.05 seconds with method rustiq, depth 133, and size 737.\n", - "Transpiled circuit index 18 (tfim) in 0.11 seconds with method rustiq, depth 25, and size 680.\n", - "Transpiled circuit index 19 (all-vib-h2co) in 27.19 seconds with method rustiq, depth 66, and size 983.\n", - "Transpiled circuit index 20 (Be2) in 0.07 seconds with method rustiq, depth 215, and size 1030.\n", - "Transpiled circuit index 21 (graph-complete_bipart) in 0.14 seconds with method rustiq, depth 328, and size 1918.\n", - "Transpiled circuit index 22 (all-vib-f2) in 0.05 seconds with method rustiq, depth 114, and size 692.\n", - "Transpiled circuit index 23 (all-vib-cyclo_propene) in 62.25 seconds with method rustiq, depth 74, and size 2348.\n", - "Transpiled circuit index 24 (TSP_Ncity-5) in 0.20 seconds with method rustiq, depth 436, and size 3605.\n", - "Transpiled circuit index 25 (H2) in 0.21 seconds with method rustiq, depth 643, and size 3476.\n", - "Transpiled circuit index 26 (uuf100-ham) in 0.24 seconds with method rustiq, depth 678, and size 6120.\n", - "Transpiled circuit index 27 (ham-graph-gnp_k-5) in 0.22 seconds with method rustiq, depth 588, and size 5241.\n", - "Transpiled circuit index 28 (tfim) in 0.34 seconds with method rustiq, depth 340, and size 5901.\n", - "Transpiled circuit index 29 (uuf100-ham) in 0.33 seconds with method rustiq, depth 881, and size 7667.\n", - "Transpiled circuit index 30 (flat100-ham) in 0.31 seconds with method rustiq, depth 279, and size 4910.\n", - "Transpiled circuit index 31 (uf100-ham) in 0.38 seconds with method rustiq, depth 1138, and size 10607.\n", - "Transpiled circuit index 32 (OH) in 0.38 seconds with method rustiq, depth 1148, and size 6512.\n", - "Transpiled circuit index 33 (HF) in 0.37 seconds with method rustiq, depth 1090, and size 6256.\n", - "Transpiled circuit index 34 (BH) in 0.37 seconds with method rustiq, depth 1148, and size 6501.\n" + "[SABRE] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=248, time=0.16s\n", + "[SABRE] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=345, size=4028, time=0.07s\n", + "[SABRE] Circuit 2 (TSP_Ncity-5): 2Q depth=187, size=2040, time=0.05s\n", + "[SABRE] Circuit 3 (tfim): 2Q depth=100, size=490, time=0.21s\n", + "[SABRE] Circuit 4 (all-vib-h2co): 2Q depth=30, size=571, time=0.18s\n", + "[SABRE] Circuit 5 (uuf100-ham): 2Q depth=414, size=4760, time=0.09s\n", + "[SABRE] Circuit 6 (uuf100-ham): 2Q depth=523, size=5648, time=0.10s\n", + "[SABRE] Circuit 7 (graph-gnp_k-4): 2Q depth=3028, size=24833, time=0.43s\n", + "[SABRE] Circuit 8 (uf100-ham): 2Q depth=700, size=8264, time=0.15s\n", + "[SABRE] Circuit 9 (uf100-ham): 2Q depth=698, size=8914, time=0.17s\n", + "[SABRE] Circuit 10 (TSP_Ncity-7): 2Q depth=432, size=6356, time=0.12s\n", + "[SABRE] Circuit 11 (all-vib-cyclo_propene): 2Q depth=30, size=1144, time=0.21s\n", + "[SABRE] Circuit 12 (TSP_Ncity-8): 2Q depth=704, size=10235, time=0.20s\n", + "[SABRE] Circuit 13 (uf100-ham): 2Q depth=2454, size=30110, time=0.47s\n", + "[SABRE] Circuit 14 (tfim): 2Q depth=245, size=3669, time=0.10s\n", + "[SABRE] Circuit 15 (flat100-ham): 2Q depth=154, size=3839, time=0.13s\n", + "[SABRE] Circuit 16 (graph-regular_reg-4): 2Q depth=863, size=14065, time=0.25s\n", + "[SABRE] Circuit 17 (tfim): 2Q depth=581, size=8786, time=0.17s\n", + "[SABRE] Circuit 18 (FH_D-1): 2Q depth=1704, size=9520, time=0.41s\n", + "[SABRE] Circuit 19 (TSP_Ncity-10): 2Q depth=1091, size=21988, time=0.44s\n", + "[SABRE] Circuit 20 (TSP_Ncity-10): 2Q depth=1091, size=21982, time=0.44s\n", + "[SABRE] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=224, size=8279, time=0.18s\n", + "[AI] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=248, time=0.17s\n", + "[AI] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=362, size=4829, time=3.09s\n", + "[AI] Circuit 2 (TSP_Ncity-5): 2Q depth=168, size=2115, time=1.37s\n", + "[AI] Circuit 3 (tfim): 2Q depth=30, size=397, time=0.33s\n", + "[AI] Circuit 4 (all-vib-h2co): 2Q depth=38, size=691, time=0.21s\n", + "[AI] Circuit 5 (uuf100-ham): 2Q depth=361, size=4909, time=3.31s\n", + "[AI] Circuit 6 (uuf100-ham): 2Q depth=462, size=5985, time=3.93s\n", + "[AI] Circuit 7 (graph-gnp_k-4): 2Q depth=3257, size=25317, time=14.85s\n", + "[AI] Circuit 8 (uf100-ham): 2Q depth=664, size=8639, time=6.17s\n", + "[AI] Circuit 9 (uf100-ham): 2Q depth=684, size=9736, time=7.00s\n", + "[AI] Circuit 10 (TSP_Ncity-7): 2Q depth=494, size=6615, time=5.20s\n", + "[AI] Circuit 11 (all-vib-cyclo_propene): 2Q depth=38, size=1443, time=0.28s\n", + "[AI] Circuit 12 (TSP_Ncity-8): 2Q depth=681, size=11315, time=9.12s\n", + "[AI] Circuit 13 (uf100-ham): 2Q depth=2260, size=31726, time=37.43s\n", + "[AI] Circuit 14 (tfim): 2Q depth=257, size=3479, time=1.44s\n", + "[AI] Circuit 15 (flat100-ham): 2Q depth=112, size=3537, time=2.29s\n", + "[AI] Circuit 16 (graph-regular_reg-4): 2Q depth=567, size=15720, time=12.35s\n", + "[AI] Circuit 17 (tfim): 2Q depth=591, size=9211, time=5.44s\n", + "[AI] Circuit 18 (FH_D-1): 2Q depth=1230, size=7646, time=4.23s\n", + "[AI] Circuit 19 (TSP_Ncity-10): 2Q depth=1094, size=22686, time=22.52s\n", + "[AI] Circuit 20 (TSP_Ncity-10): 2Q depth=1109, size=21568, time=22.51s\n", + "[AI] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=199, size=8533, time=7.78s\n", + "[Rustiq] Circuit 0 (all-vib-hc3h2cn): 2Q depth=2, size=257, time=0.15s\n", + "[Rustiq] Circuit 1 (ham-graph-gnp_k-5): 2Q depth=640, size=5797, time=0.12s\n", + "[Rustiq] Circuit 2 (TSP_Ncity-5): 2Q depth=408, size=3992, time=0.08s\n", + "[Rustiq] Circuit 3 (tfim): 2Q depth=31, size=692, time=0.11s\n", + "[Rustiq] Circuit 4 (all-vib-h2co): 2Q depth=65, size=1054, time=3.68s\n", + "[Rustiq] Circuit 5 (uuf100-ham): 2Q depth=633, size=6757, time=0.14s\n", + "[Rustiq] Circuit 6 (uuf100-ham): 2Q depth=795, size=8500, time=0.17s\n", + "[Rustiq] Circuit 7 (graph-gnp_k-4): 2Q depth=13768, size=139906, time=2.95s\n", + "[Rustiq] Circuit 8 (uf100-ham): 2Q depth=1099, size=11858, time=0.23s\n", + "[Rustiq] Circuit 9 (uf100-ham): 2Q depth=911, size=11125, time=0.22s\n", + "[Rustiq] Circuit 10 (TSP_Ncity-7): 2Q depth=1183, size=13160, time=0.26s\n", + "[Rustiq] Circuit 11 (all-vib-cyclo_propene): 2Q depth=67, size=2491, time=13.79s\n", + "[Rustiq] Circuit 12 (TSP_Ncity-8): 2Q depth=1615, size=21372, time=0.49s\n", + "[Rustiq] Circuit 13 (uf100-ham): 2Q depth=2920, size=40493, time=0.91s\n", + "[Rustiq] Circuit 14 (tfim): 2Q depth=489, size=6543, time=0.14s\n", + "[Rustiq] Circuit 15 (flat100-ham): 2Q depth=378, size=5919, time=0.14s\n", + "[Rustiq] Circuit 16 (graph-regular_reg-4): 2Q depth=12163, size=168579, time=2.89s\n", + "[Rustiq] Circuit 17 (tfim): 2Q depth=1208, size=17084, time=0.35s\n", + "[Rustiq] Circuit 18 (FH_D-1): 2Q depth=1061, size=24044, time=0.47s\n", + "[Rustiq] Circuit 19 (TSP_Ncity-10): 2Q depth=2565, size=41355, time=1.36s\n", + "[Rustiq] Circuit 20 (TSP_Ncity-10): 2Q depth=2565, size=41322, time=1.31s\n", + "[Rustiq] Circuit 21 (ham-unary-color02-queen13_13_k-4): 2Q depth=808, size=17539, time=0.58s\n" ] } ], "source": [ - "results_ham = pd.DataFrame(\n", - " columns=[\n", - " \"method\",\n", - " \"qc_name\",\n", - " \"qc_index\",\n", - " \"num_qubits\",\n", - " \"ops\",\n", - " \"depth\",\n", - " \"size\",\n", - " \"runtime\",\n", - " ]\n", - ")\n", + "results_large = pd.DataFrame(columns=columns)\n", "\n", - "tqc_sabre = capture_transpilation_metrics(\n", - " results_ham, pm_sabre, qc_ham_list, \"sabre\"\n", + "tqc_sabre_large = capture_transpilation_metrics(\n", + " results_large, pm_sabre, qc_large, \"SABRE\"\n", + ")\n", + "tqc_ai_large = capture_transpilation_metrics(\n", + " results_large, pm_ai, qc_large, \"AI\"\n", ")\n", - "tqc_ai = capture_transpilation_metrics(results_ham, pm_ai, qc_ham_list, \"ai\")\n", - "tqc_rustiq = capture_transpilation_metrics(\n", - " results_ham, pm_rustiq, qc_ham_list, \"rustiq\"\n", + "tqc_rustiq_large = capture_transpilation_metrics(\n", + " results_large, pm_rustiq, qc_large, \"Rustiq\"\n", ")" ] }, - { - "cell_type": "markdown", - "id": "ef88a127-79b8-4a53-90ef-6caa84c29e06", - "metadata": {}, - "source": [ - "Results table (skipping visualization as the output circuits are very large):" - ] - }, { "cell_type": "code", - "execution_count": 15, - "id": "bc4a7910-5e8a-48ec-bd7e-2508ae8dfccc", + "execution_count": 25, + "id": "b7c8d9e0", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " depth size runtime\n", - "method \n", - "ai 316.86 2181.26 5.97\n", - "rustiq 281.94 2268.80 3.86\n", - "sabre 337.97 2120.14 3.07\n" - ] - }, { "data": { "text/html": [ - "
\n", - "\n", - "\n", + "
\n", + " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", + " \n", + "
Mean ± Std Dev per Compilation Method (% improvement relative to SABRE)
methodqc_nameqc_indexnum_qubitsopsdepthsizeruntime
 2Q Depth% impr. (2Q Depth)Gate Count% impr. (Gate Count)Runtime (s)% impr. (Runtime (s))
Method      
0sabreall-vib-o304{'rz': 28, 'sx': 24, 'cz': 6}6580.016597AI666.4 ± 780.7+6.1% ± 20.9%9,379.3 ± 8,760.0-3.9% ± 11.0%7.8 ± 9.3-3101.7% ± 2043.7%
1sabreall-vib-c2h14{'rz': 17, 'sx': 16, 'cz': 4, 'x': 2}2391.102089Rustiq2,062.5 ± 3,631.1-154.5% ± 273.9%26,810.9 ± 43,403.4-137.6% ± 232.9%1.4 ± 3.0-527.7% ± 1429.6%
2sabreall-vib-bh22{'sx': 14, 'rz': 13, 'cz': 3}3300.011042SABRE709.1 ± 783.8+0.0% ± 0.0%9,080.4 ± 8,474.5+0.0% ± 0.0%0.2 ± 0.1+0.0% ± 0.0%
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "styled_summary_table(results_large)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "per_circuit_large_code", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", " \n", " \n", - "
Per-Circuit Comparison (first 8 circuits by qubit count)
3sabreall-vib-c2h33{'sx': 46, 'rz': 45, 'cz': 18, 'x': 6}181150.025816IndexCircuitQubits2Q Depth (SABRE)2Q Depth (AI)2Q Depth (Rustiq)Gate Count (SABRE)Gate Count (AI)Gate Count (Rustiq)Runtime (s) (SABRE)Runtime (s) (AI)Runtime (s) (Rustiq)
0all-vib-hc3h2cn242.02.02.0248.0248.0257.00.20.20.2
4sabregraph-gnp_k-244{'sx': 49, 'rz': 47, 'cz': 24, 'x': 9}241290.0230771ham-graph-gnp_k-524345.0362.0640.04028.04829.05797.00.13.10.1
...........................2TSP_Ncity-525187.0168.0408.02040.02115.03992.00.01.40.1
100rustiqflat100-ham3090{'sx': 2709, 'cz': 1379, 'rz': 817, 'x': 5}27949100.3094483tfim26100.030.031.0490.0397.0692.00.20.30.1
101rustiquf100-ham3146{'sx': 6180, 'cz': 3120, 'rz': 1303, 'x': 4}1138106070.3809774all-vib-h2co3230.038.065.0571.0691.01054.00.20.23.7
102rustiqOH3210{'sx': 3330, 'cz': 1704, 'rz': 1455, 'x': 23}114865120.3835645uuf100-ham40414.0361.0633.04760.04909.06757.00.13.30.1
103rustiqHF3310{'sx': 3213, 'cz': 1620, 'rz': 1406, 'x': 17}109062560.3685786uuf100-ham40523.0462.0795.05648.05985.08500.00.13.90.2
104rustiqBH3410{'sx': 3331, 'cz': 1704, 'rz': 1447, 'x': 19}114865010.3748227graph-gnp_k-4403028.03257.013768.024833.025317.0139906.00.414.82.9
\n", - "

105 rows × 8 columns

\n", - "
" + "\n" ], "text/plain": [ - " method qc_name qc_index num_qubits \\\n", - "0 sabre all-vib-o3 0 4 \n", - "1 sabre all-vib-c2h 1 4 \n", - "2 sabre all-vib-bh 2 2 \n", - "3 sabre all-vib-c2h 3 3 \n", - "4 sabre graph-gnp_k-2 4 4 \n", - ".. ... ... ... ... \n", - "100 rustiq flat100-ham 30 90 \n", - "101 rustiq uf100-ham 31 46 \n", - "102 rustiq OH 32 10 \n", - "103 rustiq HF 33 10 \n", - "104 rustiq BH 34 10 \n", - "\n", - " ops depth size runtime \n", - "0 {'rz': 28, 'sx': 24, 'cz': 6} 6 58 0.016597 \n", - "1 {'rz': 17, 'sx': 16, 'cz': 4, 'x': 2} 2 39 1.102089 \n", - "2 {'sx': 14, 'rz': 13, 'cz': 3} 3 30 0.011042 \n", - "3 {'sx': 46, 'rz': 45, 'cz': 18, 'x': 6} 18 115 0.025816 \n", - "4 {'sx': 49, 'rz': 47, 'cz': 24, 'x': 9} 24 129 0.023077 \n", - ".. ... ... ... ... \n", - "100 {'sx': 2709, 'cz': 1379, 'rz': 817, 'x': 5} 279 4910 0.309448 \n", - "101 {'sx': 6180, 'cz': 3120, 'rz': 1303, 'x': 4} 1138 10607 0.380977 \n", - "102 {'sx': 3330, 'cz': 1704, 'rz': 1455, 'x': 23} 1148 6512 0.383564 \n", - "103 {'sx': 3213, 'cz': 1620, 'rz': 1406, 'x': 17} 1090 6256 0.368578 \n", - "104 {'sx': 3331, 'cz': 1704, 'rz': 1447, 'x': 19} 1148 6501 0.374822 \n", - "\n", - "[105 rows x 8 columns]" + "" ] }, - "execution_count": 15, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "summary_ham = (\n", - " results_ham.groupby(\"method\")[[\"depth\", \"size\", \"runtime\"]]\n", - " .mean()\n", - " .round(2)\n", - ")\n", - "print(summary_ham)\n", - "\n", - "results_ham" + "per_circuit_comparison(results_large, num_rows=8)" ] }, { - "cell_type": "markdown", - "id": "17e7b228-01b0-4fcc-b659-9f9958f5477e", + "cell_type": "code", + "execution_count": 27, + "id": "c7d8e9f0", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "Visualize performance based on circuit index:" + "plot_transpilation_comparison(\n", + " results_large,\n", + " \"Large-Scale Hamiltonian Circuits: Compilation Comparison\",\n", + ")" ] }, { "cell_type": "code", - "execution_count": 16, - "id": "4c6b810d-a4cf-4de3-aae6-c6c1cdd83d8f", + "execution_count": 28, + "id": "pct_improvement_large", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\"Output" + "\"Output" ] }, "metadata": {}, @@ -1427,179 +2087,274 @@ } ], "source": [ - "plot_transpilation_metrics(\n", - " results_ham, \"Transpilation Metrics for Hamiltonian Circuits\"\n", + "plot_pct_improvement_vs_sabre(\n", + " results_large,\n", + " \"Large-Scale Hamiltonian Circuits\",\n", ")" ] }, { - "cell_type": "markdown", - "id": "7ad6f4ee-c9d1-4e8b-ae25-cf39672acbec", + "cell_type": "code", + "execution_count": 29, + "id": "d7e8f9a0", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "Visualize the percentage of circuits for which each method performed best." + "plot_best_method_bars(results_large)" ] }, { "cell_type": "code", - "execution_count": 17, - "id": "01b4644e-ac91-483c-944b-924f8b41718d", + "execution_count": 30, + "id": "f7a8b9c0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Best-performing methods based on depth:\n", - " ai: 16 circuit(s)\n", - " rustiq: 16 circuit(s)\n", - " sabre: 10 circuit(s)\n" + "Test circuit: tfim, 26 qubits\n", + "\n", + "Transpilation metrics for circuit index 3:\n", + " SABRE 2Q depth= 100 size= 490\n", + " AI 2Q depth= 30 size= 397\n", + " Rustiq 2Q depth= 31 size= 692\n" ] - }, - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, + } + ], + "source": [ + "# Select circuit index 6 from the large-scale transpiled circuits\n", + "test_idx_large = 3\n", + "test_circuit_large = qc_large[test_idx_large]\n", + "print(\n", + " f\"Test circuit: {test_circuit_large.name}, {test_circuit_large.num_qubits} qubits\"\n", + ")\n", + "\n", + "tqc_methods_large = {\n", + " \"SABRE\": tqc_sabre_large[test_idx_large],\n", + " \"AI\": tqc_ai_large[test_idx_large],\n", + " \"Rustiq\": tqc_rustiq_large[test_idx_large],\n", + "}\n", + "\n", + "print(f\"\\nTranspilation metrics for circuit index {test_idx_large}:\")\n", + "for method, tqc in tqc_methods_large.items():\n", + " depth_2q = tqc.depth(lambda x: x.operation.num_qubits == 2)\n", + " size = tqc.size()\n", + " print(f\" {method:8s} 2Q depth={depth_2q:5d} size={size:6d}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "3cd3afa2", + "metadata": {}, + "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Best-performing methods based on size:\n", - " sabre: 18 circuit(s)\n", - " rustiq: 14 circuit(s)\n", - " ai: 10 circuit(s)\n" + "\n", + "SABRE transpiled circuit:\n", + "OrderedDict({'sx': 212, 'rz': 163, 'cz': 104, 'x': 11})\n", + "SABRE mirror circuit count ops:\n", + "OrderedDict({'rz': 1174, 'sx': 424, 'cz': 208, 'measure': 156, 'x': 22, 'barrier': 1})\n", + "\n", + "AI transpiled circuit:\n", + "OrderedDict({'sx': 163, 'rz': 162, 'cz': 68, 'x': 4})\n", + "AI mirror circuit count ops:\n", + "OrderedDict({'rz': 976, 'sx': 326, 'measure': 156, 'cz': 136, 'x': 8, 'barrier': 1})\n", + "\n", + "Rustiq transpiled circuit:\n", + "OrderedDict({'sx': 314, 'rz': 227, 'cz': 140, 'x': 11})\n", + "Rustiq mirror circuit count ops:\n", + "OrderedDict({'rz': 1710, 'sx': 628, 'cz': 280, 'measure': 156, 'x': 22, 'barrier': 1})\n" ] - }, - { - "data": { - "text/plain": [ - "\"Output" - ] - }, - "metadata": {}, - "output_type": "display_data" } ], "source": [ - "def analyze_and_plot_best_methods(results, metric):\n", - " \"\"\"\n", - " Analyze the best-performing methods for a given metric and plot a pie chart.\n", - "\n", - " Parameters:\n", - " results (DataFrame): The input DataFrame containing method performance data.\n", - " metric (str): The metric to evaluate (\"depth\" or \"size\").\n", - " \"\"\"\n", - " method_counts = Counter()\n", - " for qc_idx, group in results.groupby(\"qc_index\"):\n", - " min_value = group[metric].min()\n", - "\n", - " # Find all methods that achieved this minimum value\n", - " best_methods = group[group[metric] == min_value][\"method\"]\n", - " # Update counts for all best methods (handling ties)\n", - " method_counts.update(best_methods)\n", - " best_method_counts = dict(\n", - " sorted(method_counts.items(), key=lambda x: x[1], reverse=True)\n", - " )\n", - "\n", - " # Print summary\n", - " print(f\"Best-performing methods based on {metric}:\")\n", - " for method, count in best_method_counts.items():\n", - " print(f\" {method}: {count} circuit(s)\")\n", - "\n", - " # Plot pie chart\n", - " num_methods = len(best_method_counts)\n", - " colors = plt.cm.viridis_r(range(0, 256, 256 // num_methods))\n", - " plt.figure(figsize=(5, 5))\n", - " plt.pie(\n", - " best_method_counts.values(),\n", - " labels=best_method_counts.keys(),\n", - " autopct=\"%1.1f%%\",\n", - " startangle=140,\n", - " wedgeprops={\"edgecolor\": \"black\"},\n", - " textprops={\"fontsize\": 10},\n", - " colors=colors,\n", - " )\n", - " plt.title(\n", - " f\"Percentage of Circuits Method Performed Best for {metric.capitalize()}\",\n", - " fontsize=12,\n", - " fontweight=\"bold\",\n", - " )\n", - " plt.show()\n", - "\n", + "pm_mirror = generate_preset_pass_manager(\n", + " optimization_level=0, backend=backend\n", + ")\n", "\n", - "analyze_and_plot_best_methods(results_ham, \"depth\")\n", - "analyze_and_plot_best_methods(results_ham, \"size\")" + "for method, tqc in tqc_methods_large.items():\n", + " # print the count ops for each circuit\n", + " mirror = tqc.copy()\n", + " mirror.compose(tqc.inverse(), inplace=True)\n", + " mirror.measure_all()\n", + " mirror = pm_mirror.run(mirror)\n", + " print(f\"\\n{method} transpiled circuit:\")\n", + " print(tqc.count_ops())\n", + " print(f\"{method} mirror circuit count ops:\")\n", + " print(mirror.count_ops())" ] }, { - "cell_type": "markdown", - "id": "e4f6aa23-d528-4d66-92e0-31d29c522792", + "cell_type": "code", + "execution_count": null, + "id": "large_hw_submit", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SABRE: submitted job d7c7eo15a5qc73dob8u0\n", + "AI: submitted job d7c7eoe5nvhs73a47cag\n", + "Rustiq: submitted job d7c7eoj0g7hs73dqefg0\n" + ] + } + ], "source": [ - "#### Analysis of Hamiltonian circuit compilation results\n", - "\n", - "In this section, we evaluate the performance of three transpilation methods — SABRE, the AI-powered transpiler, and Rustiq — on quantum circuits constructed with `PauliEvolutionGate`, which are commonly used in Hamiltonian simulation tasks.\n", - "\n", - "Rustiq performed best on average in terms of circuit depth**, achieving approximately 20% lower depth than SABRE. This is expected, as Rustiq is specifically designed to synthesize `PauliEvolutionGate` operations with optimized, low-depth decomposition strategies. Furthermore, the depth plot shows that as the circuits scale in size and complexity, Rustiq scales most effectively, maintaining significantly lower depth than both AI and SABRE on larger circuits.\n", - "\n", - "AI transpiler showed strong and consistent performance for circuit depth, consistently outperforming SABRE across most circuits. However, it incurred the highest runtime, especially on larger circuits, which may limit its practicality in time-sensitive workloads. Its scalability in runtime remains a key limitation, even though it offers solid improvements in depth.\n", - "\n", - "SABRE, while producing the highest average depth, achieved the lowest average gate count, closely followed by the AI transpiler. This aligns with the design of SABRE’s heuristic, which prioritizes minimizing gate count directly. Rustiq, despite its strength in lowering depth, had the highest average gate count, which is a notable trade-off to consider in applications where circuit size matters more than circuit duration.\n", - "\n", - "### Summary\n", - "\n", - "While the AI transpiler generally delivers better results than SABRE, particularly in circuit depth, the takeaway should not simply be \"always use the AI transpiler.\" There are important nuances to consider:\n", + "# Build mirror circuits and submit to real hardware\n", + "# The inverse may introduce gates (e.g., sxdg) not in the backend's\n", + "# basis gate set, so we re-transpile the mirror circuit.\n", + "pm_mirror = generate_preset_pass_manager(\n", + " optimization_level=0, backend=backend\n", + ")\n", "\n", - "- **AI transpiler** is typically reliable and provides depth-optimized circuits, but it comes with trade-offs in runtime, and also has other limitations, including supported coupling maps and synthesis capabilities. These are detailed in the [Qiskit Transpiler Service documentation](/docs/guides/qiskit-transpiler-service).\n", + "shots_hw = 10000\n", + "hw_jobs = {}\n", "\n", - "- In some cases, particularly with very large or hardware-specific circuits, the AI transpiler may not be as effective. In these cases, the default SABRE transpiler remains extremely reliable and can be further optimized by adjusting its parameters (see the [SABRE optimization tutorial](/docs/tutorials/transpilation-optimizations-with-sabre)).\n", + "for method, tqc in tqc_methods_large.items():\n", + " mirror = tqc.copy()\n", + " mirror.compose(tqc.inverse(), inplace=True)\n", + " mirror.measure_all()\n", "\n", - "- It's also important to consider circuit structure when choosing a method. For example, `rustiq` is purpose-built for circuits involving `PauliEvolutionGate` and often yields the best performance for Hamiltonian simulation problems.\n", + " # Re-transpile at opt level 0 to decompose into basis gates\n", + " # without changing the layout or routing\n", + " mirror = pm_mirror.run(mirror)\n", "\n", - "**Recommendation:**\n", - "> There is no one-size-fits-all transpilation strategy. Users are encouraged to understand the structure of their circuit and test multiple transpilation methods — including AI, SABRE, and specialized tools like Rustiq — to find the most efficient solution for their specific problem and hardware constraints." + " sampler = SamplerV2(mode=backend)\n", + " sampler.options.environment.job_tags = [\"TUT_CMHSC\"]\n", + " job = sampler.run([mirror], shots=shots_hw)\n", + " hw_jobs[method] = job\n", + " print(f\"{method}: submitted job {job.job_id()}\")" ] }, { - "cell_type": "markdown", - "id": "9a747477-1dc1-4706-b844-d29570fb5844", + "cell_type": "code", + "execution_count": 33, + "id": "large_hw_results", "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SABRE P(|00...0>) = 0.0584 (584/10000)\n", + "AI P(|00...0>) = 0.4189 (4189/10000)\n", + "Rustiq P(|00...0>) = 0.3150 (3150/10000)\n" + ] + } + ], "source": [ - "### Step 3: Execute using Qiskit primitives" + "# Retrieve results and compute survival probabilities\n", + "survival_probs_large = {}\n", + "\n", + "for method, job in hw_jobs.items():\n", + " result = job.result()\n", + " counts = result[0].data.meas.get_counts()\n", + "\n", + " n_qubits = backend.num_qubits\n", + " all_zeros = \"0\" * n_qubits\n", + " survival = counts.get(all_zeros, 0) / shots_hw\n", + " survival_probs_large[method] = survival\n", + " print(\n", + " f\"{method:8s} P(|00...0>) = {survival:.4f} ({counts.get(all_zeros, 0)}/{shots_hw})\"\n", + " )" ] }, { - "cell_type": "markdown", - "id": "d3d6d473-7508-4e28-82ad-eee09c9a1acf", + "cell_type": "code", + "execution_count": 34, + "id": "large_hw_plot", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Output" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "As this tutorial focuses on transpilation, no experiments are executed on a quantum device. The goal is to leverage the optimizations from Step 2 to obtain a transpiled circuit with reduced depth and gate count." + "plot_mirror_results(\n", + " tqc_methods_large, survival_probs_large, test_circuit_large.name\n", + ")" ] }, { "cell_type": "markdown", - "id": "bb4c4c67-a7e8-4ca1-a5d6-908a2e1d27e5", + "id": "sec-analysis", "metadata": {}, "source": [ - "### Step 4: Post-process and return result in desired classical format" + "## Analysis\n", + "\n", + "The benchmarks above compare SABRE, the AI transpiler, and Rustiq on Hamiltonian simulation circuits from the Hamlib collection at both small and large scale.\n", + "\n", + "### Two-qubit depth and gate count\n", + "\n", + "At large scale, SABRE and the AI transpiler produce similar results overall, with each having a slight edge in different areas: SABRE tends to achieve a slightly lower gate count on average, which aligns with how its heuristic is designed to minimize the number of inserted SWAP gates. The AI transpiler edges ahead slightly on two-qubit depth, consistent with the fact that part of its reinforcement learning training objective optimizes for circuit depth. Both methods are consistent and reliable across the full range of circuits.\n", + "\n", + "Rustiq, which is purpose-built for `PauliEvolutionGate` synthesis, performs competitively on the majority of large-scale circuits. However, its average metrics are heavily impacted by a handful of significant outliers, visible as large spikes in the compilation comparison plot. On these circuits, Rustiq produces substantially higher depth and gate count than the other methods. Without these outliers, Rustiq's average performance would be much closer to SABRE and AI. This is a pattern worth watching: as circuits grow larger and more complex, Rustiq's synthesis can occasionally produce unexpectedly large solutions.\n", + "\n", + "The key observation is that no single method dominates on every circuit. There are specific cases where each method outperforms the others, which makes it worthwhile to try all available tools and select the best result for each circuit.\n", + "\n", + "### Runtime\n", + "\n", + "SABRE is consistently the fastest method. Rustiq generally runs at a similar speed, but it can produce outliers where the compilation takes significantly longer. This is especially visible in the large-scale results, where a few circuits cause Rustiq's runtime to spike. These outliers heavily impact the average runtime, so the median may be a more representative summary for Rustiq. The AI transpiler is the slowest of the three, with runtime that grows notably on larger and more complex circuits.\n", + "\n", + "### Mirror circuit results\n", + "\n", + "The mirror circuit experiments confirm the expected trend: methods that produce lower two-qubit depth and fewer gates achieve higher survival probability under noise. This holds on both the noisy simulator (small-scale) and real hardware (large-scale).\n", + "\n", + "It is important to note that these results are for individual circuits and will vary from circuit to circuit. The mirror circuit technique is a useful proxy for measuring noise sensitivity, but it tests a doubled version of the circuit rather than the original workload.\n", + "\n", + "### Recommendations\n", + "\n", + "There is no single best transpilation strategy for all circuits. The best choice depends on the circuit structure, the optimization goal, and the available compilation time budget:\n", + "\n", + "- **SABRE** is the recommended default. It is fast, reliable, and produces strong results across a wide range of circuits. For further tuning, users can increase layout and routing trials (see the [SABRE optimization tutorial](https://quantum.cloud.ibm.com/docs/en/tutorials/transpilation-optimizations-with-sabre)).\n", + "- **AI transpiler** is worth trying when compilation time is not a constraint and you want to explore whether machine learning-based optimization can improve on the SABRE baseline for your specific circuit.\n", + "- **Rustiq** is a good option for circuits with `PauliEvolutionGate` where minimizing two-qubit depth is the top priority.\n", + "\n", + "In practice, the best approach is to run all available methods and pick the best result for each circuit. The compilation overhead of trying multiple methods is small compared to the potential improvement in execution quality on real hardware." ] }, { "cell_type": "markdown", - "id": "4aa45929-f8d9-4be8-9008-0588615fa45e", + "id": "sec-next-steps", "metadata": {}, "source": [ - "Since there is no execution for this notebook, there are no results to post-process." + "## Next steps\n", + "\n", + "If you found this tutorial useful, you might be interested in the following:\n", + "\n", + "\n", + "\n", + "- [Transpilation optimizations with SABRE](https://quantum.cloud.ibm.com/docs/en/tutorials/transpilation-optimizations-with-sabre)\n", + "- [Qiskit Transpiler Service documentation](https://quantum.cloud.ibm.com/docs/en/guides/qiskit-transpiler-service)\n", + "- [AI transpiler passes](https://quantum.cloud.ibm.com/docs/en/guides/ai-transpiler-passes)\n", + "- [Create a transpiler plugin](https://quantum.cloud.ibm.com/docs/en/guides/create-transpiler-plugin)\n", + "" ] }, { "cell_type": "markdown", - "id": "552ef44b-e567-4870-9699-d152806b9505", + "id": "sec-references", "metadata": {}, "source": [ "## References\n", @@ -1608,9 +2363,9 @@ "\n", "[2] \"Practical and efficient quantum circuit synthesis and transpiling with Reinforcement Learning\". D. Kremer, V. Villar, H. Paik, I. Duran, I. Faro, J. Cruz-Benito et al. https://arxiv.org/abs/2405.13196\n", "\n", - "[3] \"Pauli Network Circuit Synthesis with Reinforcement Learning\". A. Dubal, D. Kremer, S. Martiel, V. Villar, D. Wang, J. Cruz-Benito et al. https://arxiv.org/abs/2503.14448\n", + "[3] \"Pauli Network Circuit Synthesis with Reinforcement Learning\". A. Dubal, D. Kremer, S. Martiel, V. Villar, D. Wang, J. Cruz-Benito et al. https://arxiv.org/abs/2503.14448\n", "\n", - "[4] \"Faster and shorter synthesis of Hamiltonian simulation circuits\". T. Goubault de Brugière, S. Martiel et al. https://arxiv.org/abs/2404.03280" + "[4] \"Faster and shorter synthesis of Hamiltonian simulation circuits\". T. Goubault de Brugiere, S. Martiel et al. https://arxiv.org/abs/2404.03280" ] } ], @@ -1634,5 +2389,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 5 } diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-1.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-1.avif deleted file mode 100644 index 4541fb68046..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-1.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-3.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-3.avif deleted file mode 100644 index beb2b3a1ff5..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/01b4644e-ac91-483c-944b-924f8b41718d-3.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-1.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-1.avif deleted file mode 100644 index 1c263985a9a..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-1.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-3.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-3.avif deleted file mode 100644 index f85c49281d7..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/37924fc2-8fb6-451a-b8f9-cd79573f2384-3.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/4c6b810d-a4cf-4de3-aae6-c6c1cdd83d8f-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/4c6b810d-a4cf-4de3-aae6-c6c1cdd83d8f-0.avif deleted file mode 100644 index b46f913b8b9..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/4c6b810d-a4cf-4de3-aae6-c6c1cdd83d8f-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/66347c00-1607-4405-bb76-610690adf6b8-1.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/66347c00-1607-4405-bb76-610690adf6b8-1.avif deleted file mode 100644 index 40779e134ed..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/66347c00-1607-4405-bb76-610690adf6b8-1.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/7f7b502a-8ed6-45fa-a698-02977149e283-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/7f7b502a-8ed6-45fa-a698-02977149e283-0.avif deleted file mode 100644 index 6f7b6183949..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/7f7b502a-8ed6-45fa-a698-02977149e283-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/a6b7c8d9-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/a6b7c8d9-0.avif new file mode 100644 index 00000000000..5f0ac2a2d8d Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/a6b7c8d9-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/b4c5d6e7-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/b4c5d6e7-0.avif new file mode 100644 index 00000000000..6184ea7b4a5 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/b4c5d6e7-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/c7d8e9f0-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/c7d8e9f0-0.avif new file mode 100644 index 00000000000..f6335bf481f Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/c7d8e9f0-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d5e6f7a8-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d5e6f7a8-0.avif new file mode 100644 index 00000000000..3a2b0735ee1 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d5e6f7a8-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d7e8f9a0-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d7e8f9a0-0.avif new file mode 100644 index 00000000000..7e10854b154 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/d7e8f9a0-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/f362cdac-94d8-4cc5-85f4-015c3d9eba3a-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/f362cdac-94d8-4cc5-85f4-015c3d9eba3a-0.avif deleted file mode 100644 index 91da846530e..00000000000 Binary files a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/f362cdac-94d8-4cc5-85f4-015c3d9eba3a-0.avif and /dev/null differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/large_hw_plot-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/large_hw_plot-0.avif new file mode 100644 index 00000000000..22777d070b6 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/large_hw_plot-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_large-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_large-0.avif new file mode 100644 index 00000000000..977aafac225 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_large-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_small-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_small-0.avif new file mode 100644 index 00000000000..0cf3dcaea06 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/pct_improvement_small-0.avif differ diff --git a/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/small_step4_plot-0.avif b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/small_step4_plot-0.avif new file mode 100644 index 00000000000..313a15d07c8 Binary files /dev/null and b/public/docs/images/tutorials/compilation-methods-for-hamiltonian-simulation-circuits/extracted-outputs/small_step4_plot-0.avif differ