\n",
- "\n",
- "
\n",
+ "\n",
+ " Mean ± Std Dev per Compilation Method (% improvement relative to SABRE)\n",
" \n",
- " \n",
- " | \n",
- " method | \n",
- " qc_name | \n",
- " qc_index | \n",
- " num_qubits | \n",
- " ops | \n",
- " depth | \n",
- " size | \n",
- " runtime | \n",
- "
\n",
- " \n",
- " \n",
- " \n",
- " | 0 | \n",
- " sabre | \n",
- " SU2_10 | \n",
- " 0 | \n",
- " 10 | \n",
- " {'rz': 81, 'sx': 70, 'cz': 16} | \n",
- " 13 | \n",
- " 167 | \n",
- " 0.058845 | \n",
- "
\n",
- " \n",
- " | 1 | \n",
- " sabre | \n",
- " SU2_20 | \n",
- " 1 | \n",
- " 20 | \n",
- " {'rz': 160, 'sx': 119, 'cz': 20} | \n",
- " 20 | \n",
- " 299 | \n",
- " 0.238217 | \n",
- "
\n",
- " \n",
- " | 2 | \n",
- " sabre | \n",
- " SU2_30 | \n",
- " 2 | \n",
- " 30 | \n",
- " {'sx': 295, 'rz': 242, 'cz': 90} | \n",
- " 72 | \n",
- " 627 | \n",
- " 10.723922 | \n",
- "
\n",
" \n",
- " | 3 | \n",
- " sabre | \n",
- " SU2_40 | \n",
- " 3 | \n",
- " 40 | \n",
- " {'rz': 320, 'sx': 239, 'cz': 40} | \n",
- " 40 | \n",
- " 599 | \n",
- " 16.159262 | \n",
+ " | \n",
+ " 2Q Depth | \n",
+ " % impr. (2Q Depth) | \n",
+ " Gate Count | \n",
+ " % impr. (Gate Count) | \n",
+ " Runtime (s) | \n",
+ " % impr. (Runtime (s)) | \n",
"
\n",
" \n",
- " | 4 | \n",
- " sabre | \n",
- " SU2_50 | \n",
- " 4 | \n",
- " 50 | \n",
- " {'rz': 402, 'sx': 367, 'cz': 86} | \n",
- " 77 | \n",
- " 855 | \n",
- " 76.886604 | \n",
- "
\n",
- " \n",
- " | 5 | \n",
- " sabre | \n",
- " SU2_60 | \n",
- " 5 | \n",
- " 60 | \n",
- " {'rz': 480, 'sx': 359, 'cz': 60} | \n",
- " 60 | \n",
- " 899 | \n",
- " 86.118255 | \n",
- "
\n",
- " \n",
- " | 6 | \n",
- " sabre | \n",
- " SU2_70 | \n",
- " 6 | \n",
- " 70 | \n",
- " {'rz': 562, 'sx': 441, 'cz': 82} | \n",
- " 79 | \n",
- " 1085 | \n",
- " 94.458287 | \n",
- "
\n",
- " \n",
- " | 7 | \n",
- " sabre | \n",
- " SU2_80 | \n",
- " 7 | \n",
- " 80 | \n",
- " {'rz': 640, 'sx': 479, 'cz': 80} | \n",
- " 80 | \n",
- " 1199 | \n",
- " 69.048184 | \n",
- "
\n",
- " \n",
- " | 8 | \n",
- " sabre | \n",
- " SU2_90 | \n",
- " 8 | \n",
- " 90 | \n",
- " {'rz': 721, 'sx': 585, 'cz': 114} | \n",
- " 105 | \n",
- " 1420 | \n",
- " 88.254809 | \n",
- "
\n",
- " \n",
- " | 9 | \n",
- " sabre | \n",
- " SU2_100 | \n",
- " 9 | \n",
- " 100 | \n",
- " {'rz': 800, 'sx': 599, 'cz': 100} | \n",
- " 100 | \n",
- " 1499 | \n",
- " 83.795482 | \n",
- "
\n",
- " \n",
- " | 10 | \n",
- " ai | \n",
- " SU2_10 | \n",
- " 0 | \n",
- " 10 | \n",
- " {'rz': 81, 'sx': 71, 'cz': 16} | \n",
- " 10 | \n",
- " 168 | \n",
- " 0.171532 | \n",
- "
\n",
- " \n",
- " | 11 | \n",
- " ai | \n",
- " SU2_20 | \n",
- " 1 | \n",
- " 20 | \n",
- " {'rz': 160, 'sx': 119, 'cz': 20} | \n",
- " 20 | \n",
- " 299 | \n",
- " 0.291691 | \n",
+ " Method | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
"
\n",
+ " \n",
+ " \n",
" \n",
- " | 12 | \n",
- " ai | \n",
- " SU2_30 | \n",
- " 2 | \n",
- " 30 | \n",
- " {'sx': 243, 'rz': 242, 'cz': 63} | \n",
- " 36 | \n",
- " 548 | \n",
- " 13.555931 | \n",
+ " AI | \n",
+ " 69.3 ± 80.8 | \n",
+ " -10.0% ± 28.1% | \n",
+ " 423.8 ± 427.3 | \n",
+ " -4.4% ± 18.2% | \n",
+ " 0.2 ± 0.2 | \n",
+ " -1114.5% ± 1050.4% | \n",
"
\n",
" \n",
- " | 13 | \n",
- " ai | \n",
- " SU2_40 | \n",
- " 3 | \n",
- " 40 | \n",
- " {'rz': 320, 'sx': 239, 'cz': 40} | \n",
- " 40 | \n",
- " 599 | \n",
- " 15.952733 | \n",
+ " Rustiq | \n",
+ " 67.9 ± 80.0 | \n",
+ " -25.3% ± 85.4% | \n",
+ " 453.9 ± 489.0 | \n",
+ " -15.4% ± 50.5% | \n",
+ " 0.0 ± 0.1 | \n",
+ " +3.8% ± 50.1% | \n",
"
\n",
" \n",
- " | 14 | \n",
- " ai | \n",
- " SU2_50 | \n",
- " 4 | \n",
- " 50 | \n",
- " {'rz': 403, 'sx': 346, 'cz': 74} | \n",
- " 54 | \n",
- " 823 | \n",
- " 80.702141 | \n",
+ " SABRE | \n",
+ " 71.8 ± 89.6 | \n",
+ " +0.0% ± 0.0% | \n",
+ " 426.9 ± 446.4 | \n",
+ " +0.0% ± 0.0% | \n",
+ " 0.1 ± 0.2 | \n",
+ " +0.0% ± 0.0% | \n",
+ "
\n",
+ " \n",
+ "
\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",
+ " Per-Circuit Comparison (first 8 circuits by qubit count)\n",
+ " \n",
+ " \n",
+ " | Index | \n",
+ " Circuit | \n",
+ " Qubits | \n",
+ " 2Q Depth (SABRE) | \n",
+ " 2Q Depth (AI) | \n",
+ " 2Q Depth (Rustiq) | \n",
+ " Gate Count (SABRE) | \n",
+ " Gate Count (AI) | \n",
+ " Gate Count (Rustiq) | \n",
+ " Runtime (s) (SABRE) | \n",
+ " Runtime (s) (AI) | \n",
+ " Runtime (s) (Rustiq) | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " all-vib-bh | \n",
+ " 2 | \n",
+ " 3.0 | \n",
+ " 3.0 | \n",
+ " 3.0 | \n",
+ " 30.0 | \n",
+ " 30.0 | \n",
+ " 30.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " all-vib-c2h | \n",
+ " 3 | \n",
+ " 18.0 | \n",
+ " 18.0 | \n",
+ " 13.0 | \n",
+ " 114.0 | \n",
+ " 110.0 | \n",
+ " 74.0 | \n",
+ " 0.0 | \n",
+ " 0.2 | \n",
+ " 0.0 | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " all-vib-o3 | \n",
+ " 4 | \n",
+ " 6.0 | \n",
+ " 10.0 | \n",
+ " 13.0 | \n",
+ " 58.0 | \n",
+ " 81.0 | \n",
+ " 82.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- " | 15 | \n",
- " ai | \n",
- " SU2_60 | \n",
- " 5 | \n",
- " 60 | \n",
- " {'rz': 480, 'sx': 359, 'cz': 60} | \n",
- " 60 | \n",
- " 899 | \n",
- " 75.993404 | \n",
+ " 3 | \n",
+ " all-vib-c2h | \n",
+ " 4 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 40.0 | \n",
+ " 40.0 | \n",
+ " 37.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- " | 16 | \n",
- " ai | \n",
- " SU2_70 | \n",
- " 6 | \n",
- " 70 | \n",
- " {'rz': 563, 'sx': 442, 'cz': 82} | \n",
- " 74 | \n",
- " 1087 | \n",
- " 64.960162 | \n",
+ " 4 | \n",
+ " graph-gnp_k-2 | \n",
+ " 4 | \n",
+ " 24.0 | \n",
+ " 25.0 | \n",
+ " 31.0 | \n",
+ " 130.0 | \n",
+ " 136.0 | \n",
+ " 132.0 | \n",
+ " 0.0 | \n",
+ " 0.1 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- " | 17 | \n",
- " ai | \n",
- " SU2_80 | \n",
- " 7 | \n",
- " 80 | \n",
- " {'rz': 640, 'sx': 479, 'cz': 80} | \n",
- " 80 | \n",
- " 1199 | \n",
- " 68.253280 | \n",
+ " 5 | \n",
+ " LiH | \n",
+ " 4 | \n",
+ " 66.0 | \n",
+ " 62.0 | \n",
+ " 59.0 | \n",
+ " 289.0 | \n",
+ " 258.0 | \n",
+ " 286.0 | \n",
+ " 0.0 | \n",
+ " 0.2 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- " | 18 | \n",
- " ai | \n",
- " SU2_90 | \n",
- " 8 | \n",
- " 90 | \n",
- " {'rz': 721, 'sx': 575, 'cz': 108} | \n",
- " 90 | \n",
- " 1404 | \n",
- " 75.072412 | \n",
+ " 6 | \n",
+ " all-vib-fccf | \n",
+ " 4 | \n",
+ " 66.0 | \n",
+ " 65.0 | \n",
+ " 34.0 | \n",
+ " 339.0 | \n",
+ " 301.0 | \n",
+ " 191.0 | \n",
+ " 0.0 | \n",
+ " 0.2 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- " | 19 | \n",
- " ai | \n",
- " SU2_100 | \n",
- " 9 | \n",
- " 100 | \n",
- " {'rz': 800, 'sx': 599, 'cz': 100} | \n",
- " 100 | \n",
- " 1499 | \n",
- " 63.967446 | \n",
+ " 7 | \n",
+ " all-vib-ch2 | \n",
+ " 4 | \n",
+ " 88.0 | \n",
+ " 81.0 | \n",
+ " 49.0 | \n",
+ " 416.0 | \n",
+ " 379.0 | \n",
+ " 302.0 | \n",
+ " 0.0 | \n",
+ " 0.2 | \n",
+ " 0.0 | \n",
"
\n",
" \n",
- "
\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": [
- ""
+ ""
]
},
"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": [
+ ""
+ ]
+ },
+ "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": [
+ ""
+ ]
+ },
+ "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": [
- ""
+ ""
]
},
- "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",
+ " Mean ± Std Dev per Compilation Method (% improvement relative to SABRE)\n",
" \n",
- " \n",
- " | \n",
- " method | \n",
- " qc_name | \n",
- " qc_index | \n",
- " num_qubits | \n",
- " ops | \n",
- " depth | \n",
- " size | \n",
- " runtime | \n",
+ "
\n",
+ " | | \n",
+ " 2Q Depth | \n",
+ " % impr. (2Q Depth) | \n",
+ " Gate Count | \n",
+ " % impr. (Gate Count) | \n",
+ " Runtime (s) | \n",
+ " % impr. (Runtime (s)) | \n",
+ "
\n",
+ " \n",
+ " | Method | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
+ " | \n",
"
\n",
" \n",
" \n",
" \n",
- " | 0 | \n",
- " sabre | \n",
- " all-vib-o3 | \n",
- " 0 | \n",
- " 4 | \n",
- " {'rz': 28, 'sx': 24, 'cz': 6} | \n",
- " 6 | \n",
- " 58 | \n",
- " 0.016597 | \n",
+ " AI | \n",
+ " 666.4 ± 780.7 | \n",
+ " +6.1% ± 20.9% | \n",
+ " 9,379.3 ± 8,760.0 | \n",
+ " -3.9% ± 11.0% | \n",
+ " 7.8 ± 9.3 | \n",
+ " -3101.7% ± 2043.7% | \n",
"
\n",
" \n",
- " | 1 | \n",
- " sabre | \n",
- " all-vib-c2h | \n",
- " 1 | \n",
- " 4 | \n",
- " {'rz': 17, 'sx': 16, 'cz': 4, 'x': 2} | \n",
- " 2 | \n",
- " 39 | \n",
- " 1.102089 | \n",
+ " Rustiq | \n",
+ " 2,062.5 ± 3,631.1 | \n",
+ " -154.5% ± 273.9% | \n",
+ " 26,810.9 ± 43,403.4 | \n",
+ " -137.6% ± 232.9% | \n",
+ " 1.4 ± 3.0 | \n",
+ " -527.7% ± 1429.6% | \n",
"
\n",
" \n",
- " | 2 | \n",
- " sabre | \n",
- " all-vib-bh | \n",
- " 2 | \n",
- " 2 | \n",
- " {'sx': 14, 'rz': 13, 'cz': 3} | \n",
- " 3 | \n",
- " 30 | \n",
- " 0.011042 | \n",
+ " SABRE | \n",
+ " 709.1 ± 783.8 | \n",
+ " +0.0% ± 0.0% | \n",
+ " 9,080.4 ± 8,474.5 | \n",
+ " +0.0% ± 0.0% | \n",
+ " 0.2 ± 0.1 | \n",
+ " +0.0% ± 0.0% | \n",
"
\n",
+ " \n",
+ "
\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",
+ " Per-Circuit Comparison (first 8 circuits by qubit count)\n",
+ " \n",
" \n",
- " | 3 | \n",
- " sabre | \n",
- " all-vib-c2h | \n",
- " 3 | \n",
- " 3 | \n",
- " {'sx': 46, 'rz': 45, 'cz': 18, 'x': 6} | \n",
- " 18 | \n",
- " 115 | \n",
- " 0.025816 | \n",
+ " Index | \n",
+ " Circuit | \n",
+ " Qubits | \n",
+ " 2Q Depth (SABRE) | \n",
+ " 2Q Depth (AI) | \n",
+ " 2Q Depth (Rustiq) | \n",
+ " Gate Count (SABRE) | \n",
+ " Gate Count (AI) | \n",
+ " Gate Count (Rustiq) | \n",
+ " Runtime (s) (SABRE) | \n",
+ " Runtime (s) (AI) | \n",
+ " Runtime (s) (Rustiq) | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " all-vib-hc3h2cn | \n",
+ " 24 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 2.0 | \n",
+ " 248.0 | \n",
+ " 248.0 | \n",
+ " 257.0 | \n",
+ " 0.2 | \n",
+ " 0.2 | \n",
+ " 0.2 | \n",
"
\n",
" \n",
- " | 4 | \n",
- " sabre | \n",
- " graph-gnp_k-2 | \n",
- " 4 | \n",
- " 4 | \n",
- " {'sx': 49, 'rz': 47, 'cz': 24, 'x': 9} | \n",
- " 24 | \n",
- " 129 | \n",
- " 0.023077 | \n",
+ " 1 | \n",
+ " ham-graph-gnp_k-5 | \n",
+ " 24 | \n",
+ " 345.0 | \n",
+ " 362.0 | \n",
+ " 640.0 | \n",
+ " 4028.0 | \n",
+ " 4829.0 | \n",
+ " 5797.0 | \n",
+ " 0.1 | \n",
+ " 3.1 | \n",
+ " 0.1 | \n",
"
\n",
" \n",
- " | ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
- " ... | \n",
+ " 2 | \n",
+ " TSP_Ncity-5 | \n",
+ " 25 | \n",
+ " 187.0 | \n",
+ " 168.0 | \n",
+ " 408.0 | \n",
+ " 2040.0 | \n",
+ " 2115.0 | \n",
+ " 3992.0 | \n",
+ " 0.0 | \n",
+ " 1.4 | \n",
+ " 0.1 | \n",
"
\n",
" \n",
- " | 100 | \n",
- " rustiq | \n",
- " flat100-ham | \n",
- " 30 | \n",
- " 90 | \n",
- " {'sx': 2709, 'cz': 1379, 'rz': 817, 'x': 5} | \n",
- " 279 | \n",
- " 4910 | \n",
- " 0.309448 | \n",
+ " 3 | \n",
+ " tfim | \n",
+ " 26 | \n",
+ " 100.0 | \n",
+ " 30.0 | \n",
+ " 31.0 | \n",
+ " 490.0 | \n",
+ " 397.0 | \n",
+ " 692.0 | \n",
+ " 0.2 | \n",
+ " 0.3 | \n",
+ " 0.1 | \n",
"
\n",
" \n",
- " | 101 | \n",
- " rustiq | \n",
- " uf100-ham | \n",
- " 31 | \n",
- " 46 | \n",
- " {'sx': 6180, 'cz': 3120, 'rz': 1303, 'x': 4} | \n",
- " 1138 | \n",
- " 10607 | \n",
- " 0.380977 | \n",
+ " 4 | \n",
+ " all-vib-h2co | \n",
+ " 32 | \n",
+ " 30.0 | \n",
+ " 38.0 | \n",
+ " 65.0 | \n",
+ " 571.0 | \n",
+ " 691.0 | \n",
+ " 1054.0 | \n",
+ " 0.2 | \n",
+ " 0.2 | \n",
+ " 3.7 | \n",
"
\n",
" \n",
- " | 102 | \n",
- " rustiq | \n",
- " OH | \n",
- " 32 | \n",
- " 10 | \n",
- " {'sx': 3330, 'cz': 1704, 'rz': 1455, 'x': 23} | \n",
- " 1148 | \n",
- " 6512 | \n",
- " 0.383564 | \n",
+ " 5 | \n",
+ " uuf100-ham | \n",
+ " 40 | \n",
+ " 414.0 | \n",
+ " 361.0 | \n",
+ " 633.0 | \n",
+ " 4760.0 | \n",
+ " 4909.0 | \n",
+ " 6757.0 | \n",
+ " 0.1 | \n",
+ " 3.3 | \n",
+ " 0.1 | \n",
"
\n",
" \n",
- " | 103 | \n",
- " rustiq | \n",
- " HF | \n",
- " 33 | \n",
- " 10 | \n",
- " {'sx': 3213, 'cz': 1620, 'rz': 1406, 'x': 17} | \n",
- " 1090 | \n",
- " 6256 | \n",
- " 0.368578 | \n",
+ " 6 | \n",
+ " uuf100-ham | \n",
+ " 40 | \n",
+ " 523.0 | \n",
+ " 462.0 | \n",
+ " 795.0 | \n",
+ " 5648.0 | \n",
+ " 5985.0 | \n",
+ " 8500.0 | \n",
+ " 0.1 | \n",
+ " 3.9 | \n",
+ " 0.2 | \n",
"
\n",
" \n",
- " | 104 | \n",
- " rustiq | \n",
- " BH | \n",
- " 34 | \n",
- " 10 | \n",
- " {'sx': 3331, 'cz': 1704, 'rz': 1447, 'x': 19} | \n",
- " 1148 | \n",
- " 6501 | \n",
- " 0.374822 | \n",
+ " 7 | \n",
+ " graph-gnp_k-4 | \n",
+ " 40 | \n",
+ " 3028.0 | \n",
+ " 3257.0 | \n",
+ " 13768.0 | \n",
+ " 24833.0 | \n",
+ " 25317.0 | \n",
+ " 139906.0 | \n",
+ " 0.4 | \n",
+ " 14.8 | \n",
+ " 2.9 | \n",
"
\n",
" \n",
- "
\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": [
+ ""
+ ]
+ },
+ "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": [
- ""
+ ""
]
},
"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": [
+ ""
+ ]
+ },
+ "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": [
- ""
- ]
- },
- "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": [
- ""
- ]
- },
- "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": [
+ ""
+ ]
+ },
+ "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