{ "cells": [ { "cell_type": "markdown", "id": "1064834a", "metadata": {}, "source": [ "# Transpilation: change the circuit, preserve its meaning\n", "\n", "**Level:** Undergraduate · **Book connections:** P02, P17, P18, P19, P21\n", "\n", "After this notebook you can:\n", "\n", "- Compile a logical circuit to a defined gate set and line connectivity.\n", "- Inspect gate count, depth and layout without treating them as runtime predictions.\n", "- Verify semantic equivalence while accounting for compiler permutations.\n", "\n", "Run cells from top to bottom in the pinned environment described in `README.md`. All default execution is local. Exact amplitudes are simulator information; a QPU supplies sampled outcomes." ] }, { "cell_type": "code", "execution_count": null, "id": "d7ecca97", "metadata": {}, "outputs": [], "source": [ "from importlib.metadata import version\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from IPython.display import display\n", "from qiskit import QuantumCircuit\n", "from qiskit.quantum_info import Statevector\n", "\n", "EXPECTED = {\"qiskit\": \"2.6.1\", \"qiskit-aer\": \"0.18.2\", \"qiskit-ibm-runtime\": \"0.51.0\"}\n", "for package, expected in EXPECTED.items():\n", " actual = version(package)\n", " assert actual == expected, f\"Use the supplied environment: {package}={actual}, expected {expected}\"\n", "print(\"Verified package versions:\", EXPECTED)\n", "plt.rcParams.update({\"figure.figsize\": (8, 3.5), \"font.size\": 11, \"axes.spines.top\": False,\n", " \"axes.spines.right\": False, \"axes.prop_cycle\": plt.cycler(color=[\"#006DAE\", \"#00A6B2\", \"#9E4A78\"])})\n", "SEED = 20261106\n", "\n", "def draw(circuit):\n", " \"\"\"Render a real circuit; each wire is a qubit, time runs left to right.\"\"\"\n", " fig = circuit.draw(output=\"mpl\", fold=90, style=\"iqp\")\n", " display(fig)\n", " plt.close(fig)\n", "\n", "def book_label(qiskit_label):\n", " \"\"\"For a single register, show q0 first as in the book.\"\"\"\n", " return qiskit_label[::-1]" ] }, { "cell_type": "markdown", "id": "ec4f8776", "metadata": {}, "source": [ "**Read the wires and bit strings carefully.** Qiskit draws `q0` on the top wire, but a\n", "standard statevector basis label or measured single-register count key is written\n", "`q(n−1)…q1q0`: `q0` has integer weight $2^0$. The book often writes `q0` first.\n", "These notebooks keep native Qiskit labels in calculations and use `book_label`\n", "only for an explicitly labelled display. Reversing a label changes the display;\n", "it does not apply a SWAP gate. All count examples here use one classical register\n", "with measurement `qj → cj`; do not reverse arbitrary multi-register strings." ] }, { "cell_type": "markdown", "id": "9017ca57", "metadata": {}, "source": [ "## 1. Logical work and physical constraints\n", "\n", "A project manager can schedule the same work differently when teams have\n", "different communication links. A transpiler similarly chooses a placement,\n", "routes interactions and decomposes gates. Here the target is an invented\n", "three-qubit line, $0\\leftrightarrow1\\leftrightarrow2$. It is a reproducible\n", "teaching target, not a calibration snapshot of a real device.\n", "\n", "The input circuit needs a `CX(0,2)`. Routing must make that interaction possible.\n", "We fix both initial layout and random seeds so comparisons are meaningful." ] }, { "cell_type": "code", "execution_count": null, "id": "943ff557", "metadata": {}, "outputs": [], "source": [ "from qiskit.providers.fake_provider import GenericBackendV2\n", "from qiskit.transpiler import generate_preset_pass_manager\n", "from qiskit.quantum_info import Operator\n", "backend = GenericBackendV2(\n", " num_qubits=3, basis_gates=[\"rz\", \"sx\", \"x\", \"cx\"],\n", " coupling_map=[[0, 1], [1, 0], [1, 2], [2, 1]],\n", " noise_info=False, seed=SEED,\n", ")\n", "logical = QuantumCircuit(3)\n", "logical.h(0)\n", "logical.cx(0, 2)\n", "logical.ry(.37, 1)\n", "logical.cx(2, 1)\n", "draw(logical)\n", "compiled_circuits = {}\n", "for level in [0, 1, 2, 3]:\n", " pm = generate_preset_pass_manager(backend=backend, optimization_level=level,\n", " initial_layout=[0, 1, 2], seed_transpiler=SEED)\n", " compiled_circuits[level] = pm.run(logical)\n", "compiled = compiled_circuits[1]\n", "draw(compiled)\n", "print(\"Layout:\", compiled.layout)" ] }, { "cell_type": "markdown", "id": "86e204d2", "metadata": {}, "source": [ "## 2. Test meaning, including permutations\n", "\n", "A compiler may leave logical qubits on different output wires. Comparing raw\n", "statevector arrays can therefore compare different coordinate systems.\n", "`Operator.from_circuit` accounts for the circuit's recorded input/output layout.\n", "`equiv` compares unitaries up to a global phase. This verifies the whole unitary\n", "on this small circuit, not just its action on $|000\\rangle$.\n", "\n", "$$U_{\\text{compiled, logical coordinates}}=e^{i\\gamma}U_{\\text{original}}.$$\n", "\n", "Dense unitary verification uses $4^n$ complex entries. Use it for small components;\n", "larger programs need local properties, decomposition tests and selected experiments." ] }, { "cell_type": "code", "execution_count": null, "id": "bfe0feee", "metadata": {}, "outputs": [], "source": [ "original_operator = Operator(logical)\n", "reports = []\n", "for level, circuit in compiled_circuits.items():\n", " assert Operator.from_circuit(circuit).equiv(original_operator)\n", " for instruction in circuit.data:\n", " op = instruction.operation.name\n", " physical = [circuit.find_bit(q).index for q in instruction.qubits]\n", " assert op in backend.target.operation_names\n", " assert backend.target.instruction_supported(operation_name=op, qargs=physical)\n", " reports.append({\"optimization_level\": level, \"depth\": circuit.depth(),\n", " \"width_qubits\": circuit.num_qubits, \"CX_count\": circuit.count_ops().get(\"cx\", 0),\n", " \"total_operations\": sum(circuit.count_ops().values())})\n", "for report in reports:\n", " print(report)\n", "print(\"All four compiled unitaries preserve the original logical operation.\")" ] }, { "cell_type": "markdown", "id": "716204d4", "metadata": {}, "source": [ "## Checkpoint and practice\n", "\n", "Does the highest optimization level guarantee the shallowest circuit?\n", "**No guarantee:** heuristic compilation depends on the circuit, target and\n", "configuration. Inspect the measured resource report for this instance.\n", "Depth counts layers, while runtime also depends on durations, parallelism,\n", "scheduling, resets and measurement.\n", "\n", "1. Add the edge `0↔2`, rerun, and compare routing cost with the same seed.\n", "2. Change `initial_layout` and retain the equivalence check. Which resources change?\n", "3. Put measurements in a separate copy, transpile that copy, then check that its\n", " classical result still refers to the intended logical qubits.\n", "\n", "**Takeaway:** a successful transpile is necessary; semantic and target checks\n", "make the result reviewable." ] }, { "cell_type": "markdown", "id": "505accc2", "metadata": {}, "source": [ "## Sources\n", "\n", "- [IBM: transpile with pass managers](https://quantum.cloud.ibm.com/docs/en/guides/transpile-with-pass-managers)\n", "- [IBM: Operator API](https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.quantum_info.Operator)\n", "- [IBM: GenericBackendV2 API](https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.providers.fake_provider.GenericBackendV2)" ] } ], "metadata": { "kernelspec": { "display_name": "Quantum book (pinned Python)", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.13.2" }, "quantum_book": { "chapters": [ "P02", "P17", "P18", "P19", "P21" ], "expected_packages": { "qiskit": "2.6.1", "qiskit-aer": "0.18.2", "qiskit-ibm-runtime": "0.51.0" }, "level": "Undergraduate", "network_on_default_run": false, "verified_release_date": "2026-11-06" } }, "nbformat": 4, "nbformat_minor": 5 }