mirror of
https://github.com/saymrwulf/QuantumLearning.git
synced 2026-06-02 23:40:06 +00:00
299 lines
14 KiB
Text
299 lines
14 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Transpilation and Visualization Lab\n",
|
|
"\n",
|
|
"The lab moves from reading compiled circuits to manipulating the design choices that create them. The main skill here is attribution: which abstract choice created which compile-time cost.\n"
|
|
],
|
|
"id": "03a2ab8f"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Lab Protocol\n",
|
|
"\n",
|
|
"Keep the objective fixed while you edit structure. Then compare abstract and compiled outcomes under the same basis-gate set and coupling map. The point is not to make the compiler happy at any cost. The point is to understand what the compiler had to do on your behalf.\n"
|
|
],
|
|
"id": "3ccb42e6"
|
|
},
|
|
{
|
|
"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": "a84bcf22"
|
|
},
|
|
{
|
|
"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": "ef0ee68d"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Lab 1: Abstract Pressure\n",
|
|
"\n",
|
|
"Start from the abstract circuit with a non-local interaction. Predict which line on the diagram is causing the main routing burden before you render anything.\n"
|
|
],
|
|
"id": "874a24d5"
|
|
},
|
|
{
|
|
"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": "f9687d23"
|
|
},
|
|
{
|
|
"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='Lab 1: Abstract Pressure',\n",
|
|
" instructions='Move the long-range interaction or replace it with a local chain. Then explain how that should affect the compiled cost.',\n",
|
|
" shots=256,\n",
|
|
")\n"
|
|
],
|
|
"id": "8520c241"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'What is the key question after a non-local circuit compiles poorly on a line?', 'options': ['Which abstract design choice created avoidable routing pressure', 'Whether Jupyter plotted it correctly', 'Whether the Bell state stopped existing'], 'correct_index': 0, 'explanation': 'The first move is to identify the topology-hostile part of the design.'}, {'prompt': 'Why keep the measurement layer simple in early transpilation exercises?', 'options': ['So the compile-time consequences stay easy to attribute', 'Because measurements block transpilation', 'Because counts do not matter'], 'correct_index': 0, 'explanation': 'Clean experiments make routing cost easier to read.'}], heading='Transpilation Lab Checkpoint A')\n"
|
|
],
|
|
"id": "c397fc73"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Which abstract line in the circuit is carrying the main routing burden, and how do you know?')\n"
|
|
],
|
|
"id": "d58b6c44"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Lab 2: Render The Compiled Form Directly\n",
|
|
"\n",
|
|
"The next widget compiles inside the editable code and renders the compiled circuit itself. This is useful because it forces the diagram you see to reflect the constrained implementation rather than the abstract source.\n"
|
|
],
|
|
"id": "f1ffa8ca"
|
|
},
|
|
{
|
|
"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\\nfrom qiskit.providers.basic_provider import BasicSimulator\\nfrom quantum_learning import line_coupling_map, load_experiment_defaults, transpile_summary\\n\\ndefaults = load_experiment_defaults()\\n\\nabstract = QuantumCircuit(4, 4)\\n# [1] Start from a circuit with routing pressure.\\nabstract.h(0)\\nabstract.cx(0, 3)\\nabstract.cx(3, 1)\\nabstract.measure([0, 1, 2, 3], [0, 1, 2, 3])\\n\\n# [2] Compile to IBM-style basis gates on a line.\\nsummary = 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# [3] Render the compiled circuit directly.\\ncircuit = summary[\"compiled_circuit\"]\\n'\n",
|
|
"editable_circuit_lab(\n",
|
|
" initial_code=editable_code,\n",
|
|
" context={\"QuantumCircuit\": QuantumCircuit, \"simulate_counts\": constrained_counts},\n",
|
|
" title='Lab 2: Compiled Circuit View',\n",
|
|
" instructions='Edit the abstract source or the optimization level in the code and inspect how the compiled diagram and metrics change.',\n",
|
|
" shots=256,\n",
|
|
")\n"
|
|
],
|
|
"id": "ac3104fd"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"defaults = load_experiment_defaults()\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",
|
|
"opt0 = 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",
|
|
"opt3 = transpile_summary(\n",
|
|
" abstract,\n",
|
|
" BasicSimulator(),\n",
|
|
" basis_gates=list(defaults.transpile.basis_gates),\n",
|
|
" coupling_map=line_coupling_map(4),\n",
|
|
" optimization_level=3,\n",
|
|
")\n",
|
|
"\n",
|
|
"{\n",
|
|
" \"opt0_depth_after\": opt0[\"depth_after\"],\n",
|
|
" \"opt3_depth_after\": opt3[\"depth_after\"],\n",
|
|
" \"opt0_ops_after\": opt0[\"ops_after\"],\n",
|
|
" \"opt3_ops_after\": opt3[\"ops_after\"],\n",
|
|
"}\n"
|
|
],
|
|
"id": "d6a3417d"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Lab 3: Topology-Aware Rewrite\n",
|
|
"\n",
|
|
"Now rewrite the circuit so it cooperates with the line from the start. The point is not to erase every cost. The point is to compare compiler rescue with human design foresight.\n"
|
|
],
|
|
"id": "5a6b3922"
|
|
},
|
|
{
|
|
"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] Build a version that respects the line from the start.\\ncircuit.h(0)\\ncircuit.cx(0, 1)\\ncircuit.cx(1, 2)\\ncircuit.cx(2, 3)\\n# [2] Keep the final question fixed while you compare cost.\\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='Lab 3: Topology-Aware Rewrite',\n",
|
|
" instructions='Keep the measurement protocol stable while you compare the local-chain rewrite to the topology-hostile original.',\n",
|
|
" shots=256,\n",
|
|
")\n"
|
|
],
|
|
"id": "84f54b53"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'What is a topology-aware rewrite trying to do?', 'options': [\"Move some of the compiler's rescue work back into the original design\", 'Eliminate all two-qubit gates', 'Make every circuit look like a line'], 'correct_index': 0, 'explanation': 'A good rewrite cooperates with the topology instead of fighting it.'}, {'prompt': 'What should remain stable when you compare two compiled candidates?', 'options': ['The intended behavior and measurement protocol', 'The exact gate names in the abstract source', 'The order of markdown headings'], 'correct_index': 0, 'explanation': 'Fair comparison requires stable objectives.'}], heading='Transpilation Lab Checkpoint B')\n"
|
|
],
|
|
"id": "96c9833d"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Lab Debrief\n",
|
|
"\n",
|
|
"The lab sequence is designed to teach attribution. First you identify the abstract source of routing pressure. Then you inspect the compiler's response directly. Finally, you compare that response to a circuit that cooperates with the topology from the start. Those three views are enough to build the core engineering habit: do not treat the compiled circuit as mysterious, and do not treat the compiler as an excuse not to redesign anything upstream.\n",
|
|
"\n",
|
|
"This matters later because compile-time cost and noise cost often interact. A circuit that forces heavy routing is not only visually uglier after transpilation. It may also become a worse experimental object once noise enters. So even though this module is about compilation first, the design judgment it trains has consequences beyond compilation.\n"
|
|
],
|
|
"id": "b1472341"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Write a short memo explaining when the topology-aware rewrite is genuinely better and when it might overfit to one target.')\n"
|
|
],
|
|
"id": "534d69cd"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Describe one compiled-circuit feature that you can now interpret causally instead of treating as arbitrary clutter.')\n"
|
|
],
|
|
"id": "001a1a01"
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "QuantumLearning (.venv)",
|
|
"language": "python",
|
|
"name": "quantum-learning"
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.12"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|