mirror of
https://github.com/saymrwulf/QuantumLearning.git
synced 2026-06-06 00:03:26 +00:00
347 lines
21 KiB
Text
347 lines
21 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Transpilation and Visualization Lecture\n",
|
|
"\n",
|
|
"This module is where the course stops pretending that the clean abstract circuit is the whole engineering object. It is not. Real implementations live under basis-gate restrictions, connectivity limits, and compilation heuristics. A professional circuit designer must be able to explain what the compiler changed, why it changed it, and what design decisions upstream made that rescue more or less painful.\n"
|
|
],
|
|
"id": "2b4e241a"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Learning Objective\n",
|
|
"\n",
|
|
"By the end of this lecture, you should be able to read a compiled circuit as engineering evidence. That means naming the relevant constraint set, comparing pre- and post-transpile metrics, spotting where routing pressure entered, and proposing when a topology-aware redesign is better than simply accepting the compiler output.\n"
|
|
],
|
|
"id": "b3479252"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Why This Band Leaves The Ideal World\n",
|
|
"\n",
|
|
"The foundations band deliberately protected conceptual clarity. Now we start adding the distortions that separate a notebook toy from a realistic implementation path. The clean abstract circuit still matters. It expresses intent in the clearest possible form. But if you stop there, you will mistake intent for implementation. Transpilation is the bridge between the two.\n",
|
|
"\n",
|
|
"In a local-first course, we do not need live hardware access to learn this lesson. A basis-gate set and a coupling map are already enough to create honest engineering pressure. They force the learner to confront the difference between \"what I want\" and \"what this target can host directly.\"\n"
|
|
],
|
|
"id": "2901d298"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## The Three Main Sources Of Compile-Time Cost\n",
|
|
"\n",
|
|
"There are three big reasons a compiled circuit may grow. First, high-level operations may need to be decomposed into the basis-gate vocabulary. Second, non-local two-qubit interactions may require routing across the allowed coupling map. Third, optimization choices may restructure the circuit in ways that trade one cost for another. A useful compiled-circuit explanation names which of those mechanisms dominated.\n",
|
|
"\n",
|
|
"This matters because beginners often see the compiled circuit as arbitrary clutter. It is not arbitrary. It is a record of the costs your design and your target jointly created.\n"
|
|
],
|
|
"id": "ebeb6ddd"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Visualization Is Part Of The Analysis\n",
|
|
"\n",
|
|
"A compiled diagram is not just a prettier alternative to a metrics table. The picture helps you see where the compiler inserted work, where interactions moved, and how the logical story was rewritten into an implementable one. But the picture alone is not enough. The prose and metrics must explain what you are seeing. Good engineering combines the visual and the verbal.\n"
|
|
],
|
|
"id": "98c29258"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from pathlib import Path\n",
|
|
"import sys\n",
|
|
"\n",
|
|
"project_root = Path.cwd().resolve()\n",
|
|
"while not (project_root / \"pyproject.toml\").exists():\n",
|
|
" if project_root.parent == project_root:\n",
|
|
" raise RuntimeError(\"Could not locate the project root from this notebook.\")\n",
|
|
" project_root = project_root.parent\n",
|
|
"\n",
|
|
"src_path = project_root / \"src\"\n",
|
|
"if str(src_path) not in sys.path:\n",
|
|
" sys.path.insert(0, str(src_path))\n"
|
|
],
|
|
"id": "8ad03118"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from quantum_learning import (\n",
|
|
" build_demo_noise_model,\n",
|
|
" counts_to_probabilities,\n",
|
|
" draw_circuit,\n",
|
|
" editable_circuit_lab,\n",
|
|
" line_coupling_map,\n",
|
|
" load_experiment_defaults,\n",
|
|
" plot_counts,\n",
|
|
" plot_probabilities,\n",
|
|
" quiz_block,\n",
|
|
" reflection_box,\n",
|
|
" simulate_counts,\n",
|
|
" statevector_probabilities,\n",
|
|
" step_reference_table,\n",
|
|
" transpile_summary,\n",
|
|
")\n",
|
|
"from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister\n",
|
|
"from qiskit.providers.basic_provider import BasicSimulator\n"
|
|
],
|
|
"id": "499342c3"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Code-To-Diagram Anchor\n",
|
|
"\n",
|
|
"The anchor example creates a small amount of non-local pressure on a four-qubit line. Read the marker table first, then compare the abstract and compiled views.\n"
|
|
],
|
|
"id": "9c3e82bf"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"step_reference_table([{'marker': '[1]', 'code_focus': 'Write the abstract circuit in the cleanest form for the algorithmic intention.', 'diagram_effect': 'The first diagram expresses what you want, not yet what a constrained backend can host.', 'why_it_matters': 'Design begins with intent, but intent is not the end of the story.'}, {'marker': '[2]', 'code_focus': 'Declare a basis-gate set and a line coupling map that mimic local hardware pressure.', 'diagram_effect': 'The implicit device assumptions become explicit engineering constraints.', 'why_it_matters': 'Transpilation only becomes intelligible when the constraints are visible.'}, {'marker': '[3]', 'code_focus': 'Compile the circuit and inspect how the depth, size, and gate counts changed.', 'diagram_effect': 'The compiled diagram usually has more structure than the abstract one.', 'why_it_matters': 'Routing and basis conversion create real implementation cost.'}, {'marker': '[4]', 'code_focus': 'Compare the compiled rewrite to a topology-aware redesign.', 'diagram_effect': 'You can see whether the compiler had to rescue a poor abstract layout.', 'why_it_matters': 'Professional design does not stop at letting the compiler suffer in silence.'}])\n"
|
|
],
|
|
"id": "e2a5be05"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"defaults = load_experiment_defaults()\n",
|
|
"constrained_counts = lambda circuit, shots=256: simulate_counts(\n",
|
|
" circuit,\n",
|
|
" shots=shots,\n",
|
|
" basis_gates=list(defaults.transpile.basis_gates),\n",
|
|
" coupling_map=line_coupling_map(circuit.num_qubits),\n",
|
|
")\n",
|
|
"\n",
|
|
"editable_code = '\\nfrom qiskit import QuantumCircuit\\n\\ncircuit = QuantumCircuit(4, 4)\\n# [1] Express a non-local interaction cleanly at the abstract level.\\ncircuit.h(0)\\ncircuit.cx(0, 3)\\ncircuit.cx(3, 1)\\n# [2] Keep the measurement layer simple so the compilation changes are easy to inspect.\\ncircuit.measure([0, 1, 2, 3], [0, 1, 2, 3])\\n'\n",
|
|
"editable_circuit_lab(\n",
|
|
" initial_code=editable_code,\n",
|
|
" context={\"QuantumCircuit\": QuantumCircuit, \"simulate_counts\": constrained_counts},\n",
|
|
" title='Transpilation Anchor: Abstract Circuit',\n",
|
|
" instructions='Edit the abstract circuit first. Ask which line is likely to trigger routing pressure on a four-qubit line.',\n",
|
|
" shots=256,\n",
|
|
")\n"
|
|
],
|
|
"id": "0af1fec0"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"defaults = load_experiment_defaults()\n",
|
|
"\n",
|
|
"abstract = QuantumCircuit(4, 4)\n",
|
|
"abstract.h(0)\n",
|
|
"abstract.cx(0, 3)\n",
|
|
"abstract.cx(3, 1)\n",
|
|
"abstract.measure([0, 1, 2, 3], [0, 1, 2, 3])\n",
|
|
"\n",
|
|
"summary = transpile_summary(\n",
|
|
" abstract,\n",
|
|
" BasicSimulator(),\n",
|
|
" basis_gates=list(defaults.transpile.basis_gates),\n",
|
|
" coupling_map=line_coupling_map(4),\n",
|
|
" optimization_level=0,\n",
|
|
")\n",
|
|
"\n",
|
|
"{\n",
|
|
" \"depth_before\": summary[\"depth_before\"],\n",
|
|
" \"depth_after\": summary[\"depth_after\"],\n",
|
|
" \"size_before\": summary[\"size_before\"],\n",
|
|
" \"size_after\": summary[\"size_after\"],\n",
|
|
" \"ops_before\": summary[\"ops_before\"],\n",
|
|
" \"ops_after\": summary[\"ops_after\"],\n",
|
|
"}\n"
|
|
],
|
|
"id": "46a9cfa9"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"draw_circuit(summary['compiled_circuit'])\n"
|
|
],
|
|
"id": "5f37f2a6"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'Why is a clean abstract circuit still worth writing before compilation?', 'options': ['Because intent should be explicit before constraint-handling enters', 'Because transpilation always improves the algorithm', 'Because basis gates do not matter'], 'correct_index': 0, 'explanation': 'The abstract circuit states the target behavior in the clearest form.'}, {'prompt': 'What does a coupling map tell you?', 'options': ['Which two-qubit interactions are directly allowed by the local topology', 'How many markdown cells the notebook should contain', 'The final shot count'], 'correct_index': 0, 'explanation': 'Topology pressure is one of the main reasons compilation rewrites circuits.'}, {'prompt': 'Why do compiled circuits often get deeper?', 'options': ['Routing and basis conversion add implementation work', 'The statevector got larger', 'Measurements duplicate gates automatically'], 'correct_index': 0, 'explanation': 'Compilation cost is usually structural, not mysterious.'}], heading='Transpilation Checkpoint A')\n"
|
|
],
|
|
"id": "172f5b2b"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Reading The Rewrite\n",
|
|
"\n",
|
|
"A useful transpilation explanation usually starts with the constraint set, not with the picture. State the basis gates. State the topology. Then explain which abstract feature was hostile to those constraints. Only after that should you interpret the compiled rewrite. This order prevents magical thinking. The compiler did not \"decide to make the circuit ugly.\" It paid the bills created by the design and the target.\n"
|
|
],
|
|
"id": "a8b9f1c4"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## When Human Redesign Beats Compiler Rescue\n",
|
|
"\n",
|
|
"The compiler is powerful, but it is not a substitute for circuit judgment. If the compiled rewrite reveals that your abstract circuit is constantly asking for long-range interactions on a line, that is not only the compiler's problem. It may be a design smell. A topology-aware alternative that carries the same intent more cooperatively can be the better engineering solution.\n"
|
|
],
|
|
"id": "4a38f9d1"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## A Practical Reading Order For Compiled Circuits\n",
|
|
"\n",
|
|
"When you inspect a compiled circuit, use a fixed reading order. First, state the abstract objective in one sentence so the target remains visible. Second, name the active constraints: basis gates, coupling map, and any optimization-level choice. Third, look at the largest metric changes, because they tell you where to focus. Fourth, inspect the compiled diagram and ask which part of the change came from basis conversion and which part came from routing pressure. Finally, ask whether the abstract circuit itself is topology-hostile in a way that a human redesign could soften. That order keeps the analysis causal instead of aesthetic.\n",
|
|
"\n",
|
|
"This reading order is valuable because compiled circuits are easy to misread emotionally. Beginners often see more gates and conclude that the compiler is doing arbitrary damage. The disciplined alternative is to treat every extra structure as a bill and then ask which design choice or constraint generated it.\n"
|
|
],
|
|
"id": "2f4d631d"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Why This Module Uses Local Constraints Instead Of Cloud Hardware\n",
|
|
"\n",
|
|
"A local-first transpilation module is not a compromise. It is a teaching choice. Basis-gate sets and coupling maps already expose the core compile-time tensions without making the learner chase moving remote targets. That makes the reasoning cleaner. Once the learner can already describe routing pressure, depth inflation, and topology-aware redesign locally, later encounters with richer hardware data will make more sense rather than less.\n"
|
|
],
|
|
"id": "a87af1cc"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Three Common Compile-Time Failure Patterns\n",
|
|
"\n",
|
|
"The first failure pattern is hidden non-locality: the abstract circuit looks innocent until you notice that its most important interaction skips over intermediate qubits that the target topology cannot ignore. The second failure pattern is basis blindness: the author uses a gate vocabulary that is convenient for explanation but never asks what decomposition cost will be paid in the target basis. The third failure pattern is compiler overtrust: the learner sees a much larger compiled circuit and treats the compiler as a black box instead of asking what abstract design choice created the expense. These three patterns are common enough that they are worth naming explicitly now.\n",
|
|
"\n",
|
|
"Naming them matters because diagnosis becomes faster. When a compiled circuit suddenly grows, you no longer have to stare at it vaguely. You can ask: am I paying for hidden non-locality, basis mismatch, or both? That is already a much stronger engineering starting point.\n"
|
|
],
|
|
"id": "4e3834ea"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## How To Write A Useful Transpilation Memo\n",
|
|
"\n",
|
|
"A useful transpilation memo is short but structured. Start with the abstract objective. Then state the target assumptions: basis gates, topology, and optimization setting. Report the main metric changes. After that, name the causal story of the rewrite in plain language: which interaction triggered routing, which operation needed decomposition, and whether the compiler mostly rearranged cost or mostly introduced new cost. Finally, state the design judgment. Should the original circuit stay as it is, or is there a topology-aware rewrite that better respects the target?\n",
|
|
"\n",
|
|
"This memo format is more important than it may look. Later, when circuits become algorithmic and hardware-aware at the same time, you will need a compact way to talk about compilation without collapsing into either hand-waving or giant changelogs. The discipline starts here.\n"
|
|
],
|
|
"id": "d28a7747"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Case Study Thinking Instead Of Feature Spotting\n",
|
|
"\n",
|
|
"A weak learner opens a compiled circuit and starts feature spotting: there is a SWAP-like pattern here, more CNOTs there, maybe a few extra single-qubit rotations at the edges. That is not useless, but it is not enough. A stronger learner treats the whole example as a case study. What was the abstract request? What target assumptions made that request expensive? Which part of the rewrite is unavoidable, and which part reflects a poor initial layout? What would another abstract design have changed? Those are the questions that turn isolated compile artifacts into a coherent engineering story.\n",
|
|
"\n",
|
|
"This case-study style matters because the same visual feature can mean different things in different contexts. An extra block of single-qubit gates might be harmless basis conversion in one example and evidence of a poor abstraction choice in another. A cluster of two-qubit operations might represent inevitable routing on one topology and avoidable hostility on another. The compiled circuit only becomes legible when you keep asking what problem it is solving and what bill it is paying.\n"
|
|
],
|
|
"id": "260353c6"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Why Design Accountability Still Belongs To The Human\n",
|
|
"\n",
|
|
"It is easy to slip into a passive relationship with the transpiler. After all, it can usually produce some valid compiled circuit. But validity is not the only engineering standard. You are still responsible for whether the abstract design invites needless routing, whether the measurement protocol stayed stable enough for fair comparison, and whether the chosen target assumptions were made explicit enough that another person could reproduce the analysis. In other words, compilation does not erase authorship. It only makes authorship more visible.\n",
|
|
"\n",
|
|
"This is one reason the course keeps pairing compiled diagrams with redesign prompts. The learner should not leave this module thinking that transpilation is something that happens to circuits from the outside. It is part of the circuit-design conversation. The abstract circuit, the target constraints, and the compiler together create the final implementation path. Professional judgment means taking ownership of all three.\n"
|
|
],
|
|
"id": "bd3887e3"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'What is the main reason to inspect the compiled circuit directly?', 'options': ['To see what tradeoffs the transpiler actually paid on your behalf', 'To replace all analysis with pictures', 'To avoid basis gates altogether'], 'correct_index': 0, 'explanation': 'The compiled diagram is engineering evidence, not decoration.'}, {'prompt': 'When should you redesign a circuit instead of only accepting the compiler output?', 'options': ['When the compiled rewrite reveals avoidable topology hostility in the abstract design', 'Never, because the transpiler is always optimal', 'Only after hardware access'], 'correct_index': 0, 'explanation': 'Topology-aware redesign is part of professional circuit work.'}, {'prompt': 'Why compare optimization levels in a teaching notebook?', 'options': ['To learn that compilation is a design space with tradeoffs, not a single magic button', 'Because optimization level changes the number of qubits', 'Because lower levels disable measurement'], 'correct_index': 0, 'explanation': 'Compilation choices are part of the engineering story.'}], heading='Transpilation Checkpoint B')\n"
|
|
],
|
|
"id": "bfe4fd56"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Write a short paragraph explaining the difference between an abstract circuit being logically clear and the same circuit being implementation-friendly.')\n"
|
|
],
|
|
"id": "b2744281"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Name one sign in a compiled circuit that would make you consider an upstream redesign instead of only accepting the transpiler output.')\n"
|
|
],
|
|
"id": "bf04a02c"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Mastery Gate\n",
|
|
"\n",
|
|
"You are ready to leave this lecture only if you can state the constraint set, describe the compiled rewrite in plain engineering language, and propose one topology-aware alternative when the compiler had to rescue poor structure.\n"
|
|
],
|
|
"id": "5684230e"
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "QuantumLearning (.venv)",
|
|
"language": "python",
|
|
"name": "quantum-learning"
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.12"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|