mirror of
https://github.com/saymrwulf/QuantumLearning.git
synced 2026-05-14 20:58:00 +00:00
2729 lines
132 KiB
Python
2729 lines
132 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
from textwrap import dedent
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
NOTEBOOKS = ROOT / "notebooks"
|
|
MODULE_01_DIR = (
|
|
NOTEBOOKS / "qiskit_engineering" / "module_01_circuit_construction_and_analysis"
|
|
)
|
|
MODULE_02_DIR = (
|
|
NOTEBOOKS / "qiskit_engineering" / "module_02_transpilation_and_visualization"
|
|
)
|
|
MODULE_03_DIR = (
|
|
NOTEBOOKS / "qiskit_engineering" / "module_03_simulation_and_noise_models"
|
|
)
|
|
|
|
|
|
def markdown_cell(text: str) -> dict:
|
|
cleaned = "\n".join(line.rstrip() for line in dedent(text).strip("\n").splitlines())
|
|
return {
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [line + "\n" for line in cleaned.splitlines()],
|
|
}
|
|
|
|
|
|
def code_cell(source: str) -> dict:
|
|
cleaned = dedent(source).strip("\n")
|
|
return {
|
|
"cell_type": "code",
|
|
"execution_count": None,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [line + "\n" for line in cleaned.splitlines()],
|
|
}
|
|
|
|
|
|
def _cell_id(index: int, cell: dict) -> str:
|
|
source = "".join(cell.get("source", []))
|
|
digest = hashlib.sha1(f"{cell.get('cell_type', 'cell')}:{index}:{source}".encode()).hexdigest()
|
|
return digest[:8]
|
|
|
|
|
|
def notebook(cells: list[dict]) -> dict:
|
|
normalized_cells = []
|
|
for index, cell in enumerate(cells):
|
|
payload = dict(cell)
|
|
payload.setdefault("id", _cell_id(index, payload))
|
|
normalized_cells.append(payload)
|
|
return {
|
|
"cells": normalized_cells,
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "QuantumLearning (.venv)",
|
|
"language": "python",
|
|
"name": "quantum-learning",
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.12",
|
|
},
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5,
|
|
}
|
|
|
|
|
|
def write_notebook(path: Path, payload: dict) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(payload, indent=2) + "\n")
|
|
|
|
|
|
def quiz_code(questions: list[dict], heading: str) -> dict:
|
|
return code_cell(f"quiz_block({questions!r}, heading={heading!r})")
|
|
|
|
|
|
def reflection_code(prompt: str) -> dict:
|
|
return code_cell(f"reflection_box({prompt!r})")
|
|
|
|
|
|
def compose_code(*blocks: str) -> str:
|
|
cleaned_blocks = []
|
|
for block in blocks:
|
|
cleaned = dedent(block).strip("\n")
|
|
if cleaned:
|
|
cleaned_blocks.append(cleaned)
|
|
return "\n\n".join(cleaned_blocks)
|
|
|
|
|
|
def editable_lab_code(
|
|
initial_code: str,
|
|
*,
|
|
title: str,
|
|
instructions: str,
|
|
context_source: str = "",
|
|
context_name: str = "simulate_counts",
|
|
shots: int = 256,
|
|
) -> dict:
|
|
return code_cell(
|
|
compose_code(
|
|
context_source,
|
|
f"""
|
|
editable_code = {initial_code!r}
|
|
editable_circuit_lab(
|
|
initial_code=editable_code,
|
|
context={{"QuantumCircuit": QuantumCircuit, "simulate_counts": {context_name}}},
|
|
title={title!r},
|
|
instructions={instructions!r},
|
|
shots={shots},
|
|
)
|
|
""",
|
|
)
|
|
)
|
|
|
|
|
|
SETUP = """
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
project_root = Path.cwd().resolve()
|
|
while not (project_root / "pyproject.toml").exists():
|
|
if project_root.parent == project_root:
|
|
raise RuntimeError("Could not locate the project root from this notebook.")
|
|
project_root = project_root.parent
|
|
|
|
src_path = project_root / "src"
|
|
if str(src_path) not in sys.path:
|
|
sys.path.insert(0, str(src_path))
|
|
"""
|
|
|
|
|
|
COMMON_IMPORTS = """
|
|
from quantum_learning import (
|
|
build_demo_noise_model,
|
|
counts_to_probabilities,
|
|
draw_circuit,
|
|
editable_circuit_lab,
|
|
line_coupling_map,
|
|
load_experiment_defaults,
|
|
plot_counts,
|
|
plot_probabilities,
|
|
quiz_block,
|
|
reflection_box,
|
|
simulate_counts,
|
|
statevector_probabilities,
|
|
step_reference_table,
|
|
transpile_summary,
|
|
)
|
|
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
|
|
from qiskit.providers.basic_provider import BasicSimulator
|
|
"""
|
|
|
|
|
|
CONSTRUCTION_STEP_REFS = [
|
|
{
|
|
"marker": "[1]",
|
|
"code_focus": "Declare named quantum and classical registers that express circuit roles instead of anonymous wire positions.",
|
|
"diagram_effect": "The rendered circuit now carries semantic names such as data, syndrome, and readout.",
|
|
"why_it_matters": "Named registers turn diagrams into interfaces and make later review far less error prone.",
|
|
},
|
|
{
|
|
"marker": "[2]",
|
|
"code_focus": "Build a preparation layer that creates the main data-state relationship.",
|
|
"diagram_effect": "The first part of the diagram clearly separates state preparation from later bookkeeping.",
|
|
"why_it_matters": "Good engineering keeps the causal burden of each region readable.",
|
|
},
|
|
{
|
|
"marker": "[3]",
|
|
"code_focus": "Use a dedicated wire for a parity-style check rather than mixing all roles onto one register.",
|
|
"diagram_effect": "The circuit exposes intent: data wires store the payload, the extra wire stores a check.",
|
|
"why_it_matters": "Role separation is the start of reusable circuit architecture.",
|
|
},
|
|
{
|
|
"marker": "[4]",
|
|
"code_focus": "Measure into an explicitly ordered classical register.",
|
|
"diagram_effect": "The reporting layer is visible and auditable rather than implicit.",
|
|
"why_it_matters": "Engineering quality includes the evidence path, not only the quantum body of the circuit.",
|
|
},
|
|
]
|
|
|
|
|
|
CONSTRUCTION_ANCHOR = """
|
|
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
|
|
|
|
data = QuantumRegister(2, "data")
|
|
syndrome = QuantumRegister(1, "syndrome")
|
|
readout = ClassicalRegister(3, "readout")
|
|
circuit = QuantumCircuit(data, syndrome, readout)
|
|
|
|
# [1] Prepare a named two-qubit data block.
|
|
circuit.h(data[0])
|
|
circuit.cx(data[0], data[1])
|
|
|
|
# [2] Route a parity-style question into the syndrome wire.
|
|
circuit.cx(data[0], syndrome[0])
|
|
circuit.cx(data[1], syndrome[0])
|
|
|
|
# [3] Keep the reporting layer explicit.
|
|
circuit.measure(data[0], readout[0])
|
|
circuit.measure(data[1], readout[1])
|
|
circuit.measure(syndrome[0], readout[2])
|
|
"""
|
|
|
|
|
|
CONSTRUCTION_BLOCK_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
def bell_block() -> QuantumCircuit:
|
|
block = QuantumCircuit(2, name="bell_prep")
|
|
# [1] Encapsulate the preparation story.
|
|
block.h(0)
|
|
block.cx(0, 1)
|
|
return block
|
|
|
|
def readout_block() -> QuantumCircuit:
|
|
block = QuantumCircuit(2, 2, name="readout")
|
|
# [2] Keep the question and the reporting layer reusable too.
|
|
block.measure([0, 1], [0, 1])
|
|
return block
|
|
|
|
circuit = QuantumCircuit(2, 2)
|
|
# [3] Compose blocks instead of retyping the same logic inline.
|
|
circuit.compose(bell_block(), inplace=True)
|
|
circuit.compose(readout_block(), inplace=True)
|
|
"""
|
|
|
|
|
|
CONSTRUCTION_METRICS_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
def candidate(extra_layer: bool = False) -> QuantumCircuit:
|
|
circuit = QuantumCircuit(3, 3)
|
|
# [1] Create a clean backbone.
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
circuit.cx(1, 2)
|
|
# [2] Toggle the extra layer to inspect the cost of a design choice.
|
|
if extra_layer:
|
|
circuit.cx(0, 2)
|
|
# [3] Keep the reporting layer stable while you compare.
|
|
circuit.measure([0, 1, 2], [0, 1, 2])
|
|
return circuit
|
|
|
|
circuit = candidate(extra_layer=True)
|
|
"""
|
|
|
|
|
|
TRANSPILATION_STEP_REFS = [
|
|
{
|
|
"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.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_ABSTRACT_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
circuit = QuantumCircuit(4, 4)
|
|
# [1] Express a non-local interaction cleanly at the abstract level.
|
|
circuit.h(0)
|
|
circuit.cx(0, 3)
|
|
circuit.cx(3, 1)
|
|
# [2] Keep the measurement layer simple so the compilation changes are easy to inspect.
|
|
circuit.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
|
"""
|
|
|
|
|
|
TRANSPILATION_COMPILED_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
from qiskit.providers.basic_provider import BasicSimulator
|
|
from quantum_learning import line_coupling_map, load_experiment_defaults, transpile_summary
|
|
|
|
defaults = load_experiment_defaults()
|
|
|
|
abstract = QuantumCircuit(4, 4)
|
|
# [1] Start from a circuit with routing pressure.
|
|
abstract.h(0)
|
|
abstract.cx(0, 3)
|
|
abstract.cx(3, 1)
|
|
abstract.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
|
|
|
# [2] Compile to IBM-style basis gates on a line.
|
|
summary = transpile_summary(
|
|
abstract,
|
|
BasicSimulator(),
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(4),
|
|
optimization_level=0,
|
|
)
|
|
|
|
# [3] Render the compiled circuit directly.
|
|
circuit = summary["compiled_circuit"]
|
|
"""
|
|
|
|
|
|
TRANSPILATION_TOPOLOGY_AWARE_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
circuit = QuantumCircuit(4, 4)
|
|
# [1] Build a version that respects the line from the start.
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
circuit.cx(1, 2)
|
|
circuit.cx(2, 3)
|
|
# [2] Keep the final question fixed while you compare cost.
|
|
circuit.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
|
"""
|
|
|
|
|
|
NOISE_STEP_REFS = [
|
|
{
|
|
"marker": "[1]",
|
|
"code_focus": "Prepare a small circuit with a clean ideal story that can be simulated exactly.",
|
|
"diagram_effect": "The circuit is shallow enough that the intended behavior is easy to articulate.",
|
|
"why_it_matters": "Noise reasoning is impossible if the ideal reference story is already blurry.",
|
|
},
|
|
{
|
|
"marker": "[2]",
|
|
"code_focus": "Choose the right view: statevector, shot counts, or a noisy local model.",
|
|
"diagram_effect": "The diagram may stay the same while the lens on it changes.",
|
|
"why_it_matters": "Different questions require different simulators and evidence forms.",
|
|
},
|
|
{
|
|
"marker": "[3]",
|
|
"code_focus": "Inject a local noise profile and compare the distribution to the ideal result.",
|
|
"diagram_effect": "The circuit picture is unchanged, but the evidence becomes visibly distorted.",
|
|
"why_it_matters": "Noise belongs in the interpretation layer even when the diagram looks familiar.",
|
|
},
|
|
{
|
|
"marker": "[4]",
|
|
"code_focus": "Stress the design with extra two-qubit structure and see how robustness changes.",
|
|
"diagram_effect": "A slightly deeper circuit may look only a little larger but degrade much more under noise.",
|
|
"why_it_matters": "Structure and robustness are coupled design questions.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_IDEAL_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
circuit = QuantumCircuit(2, 2)
|
|
# [1] Prepare the clean Bell-style reference.
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
# [2] Keep the reporting layer fixed.
|
|
circuit.measure([0, 1], [0, 1])
|
|
"""
|
|
|
|
|
|
NOISE_STRESSED_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
circuit = QuantumCircuit(3, 3)
|
|
# [1] Prepare a shallow correlated backbone.
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
circuit.cx(1, 2)
|
|
# [2] Add an extra entangling layer to stress the design.
|
|
circuit.cx(0, 2)
|
|
# [3] Measure all wires consistently.
|
|
circuit.measure([0, 1, 2], [0, 1, 2])
|
|
"""
|
|
|
|
|
|
NOISE_ROBUST_EDITABLE = """
|
|
from qiskit import QuantumCircuit
|
|
|
|
circuit = QuantumCircuit(3, 3)
|
|
# [1] Build the leaner candidate.
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
circuit.cx(1, 2)
|
|
# [2] Leave the extra long-range entangler out and compare the evidence.
|
|
circuit.measure([0, 1, 2], [0, 1, 2])
|
|
"""
|
|
|
|
|
|
CONSTRUCTION_QUIZ_A = [
|
|
{
|
|
"prompt": "What is the main engineering value of named registers?",
|
|
"options": [
|
|
"They turn wires into role-bearing interfaces instead of anonymous positions",
|
|
"They always reduce depth",
|
|
"They remove the need for measurements",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Named registers improve legibility and make later review far cleaner.",
|
|
},
|
|
{
|
|
"prompt": "Why separate a syndrome-style wire from the data wires in an introductory engineering example?",
|
|
"options": [
|
|
"To make the role structure visible early",
|
|
"Because Qiskit requires one extra qubit in every circuit",
|
|
"Only to make the diagram longer",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Role separation is the foundation of reusable circuit architecture.",
|
|
},
|
|
{
|
|
"prompt": "What does a circuit metric such as depth help you talk about?",
|
|
"options": [
|
|
"The implementation cost of a design choice",
|
|
"The color of the plotted bars",
|
|
"Whether the statevector exists",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Metrics are evidence for engineering tradeoffs, not decoration.",
|
|
},
|
|
]
|
|
|
|
|
|
CONSTRUCTION_QUIZ_B = [
|
|
{
|
|
"prompt": "What is the strongest reason to refactor repeated gate sequences into builder functions or blocks?",
|
|
"options": [
|
|
"To make intent, reuse, and comparison explicit",
|
|
"To avoid ever reading the circuit diagram again",
|
|
"To bypass measurement wiring",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Abstraction should clarify the design and reduce fragile duplication.",
|
|
},
|
|
{
|
|
"prompt": "When is abstraction boundary design going wrong?",
|
|
"options": [
|
|
"When the helper hides the causal burden so thoroughly that the circuit story becomes harder to review",
|
|
"When the circuit becomes shorter",
|
|
"When a block has a name",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Good abstractions compress repetition without erasing meaning.",
|
|
},
|
|
{
|
|
"prompt": "Why keep the classical register explicit in a construction module?",
|
|
"options": [
|
|
"Because engineering includes how evidence is reported, not only how states are prepared",
|
|
"Because classical bits change the quantum state directly",
|
|
"Because count previews need more memory",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The evidence path is part of circuit design quality.",
|
|
},
|
|
]
|
|
|
|
|
|
CONSTRUCTION_LAB_QUIZ_A = [
|
|
{
|
|
"prompt": "What is the best use of a first editable construction lab?",
|
|
"options": [
|
|
"Change one role-bearing design decision and inspect how the structure and evidence respond",
|
|
"Randomly add gates until the diagram looks complicated",
|
|
"Avoid metrics until the capstone",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Engineering grows through controlled edits, not decorative complexity.",
|
|
},
|
|
{
|
|
"prompt": "Why compare a flat circuit and a block-based circuit for the same behavior?",
|
|
"options": [
|
|
"To judge whether the abstraction actually earned its existence",
|
|
"Because one of them must be numerically wrong",
|
|
"Because Qiskit disallows reuse otherwise",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Abstractions should be reviewed, not assumed good by default.",
|
|
},
|
|
]
|
|
|
|
|
|
CONSTRUCTION_LAB_QUIZ_B = [
|
|
{
|
|
"prompt": "What should stay fixed while you compare candidate constructions?",
|
|
"options": [
|
|
"The intended behavior and reporting layer",
|
|
"The exact number of comments",
|
|
"The notebook theme",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Comparison only works when the engineering objective stays stable.",
|
|
},
|
|
{
|
|
"prompt": "What is a useful first explanation after circuit metrics worsen?",
|
|
"options": [
|
|
"Name the structural choice that caused the cost increase",
|
|
"Assume Qiskit made a mistake",
|
|
"Ignore the metrics if the counts still look plausible",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Metrics should be tied back to concrete design moves.",
|
|
},
|
|
]
|
|
|
|
|
|
CONSTRUCTION_PROBLEM_SETS = [
|
|
(
|
|
"Construction Problem Set A",
|
|
[
|
|
{
|
|
"prompt": "Why does this course treat registers as more than container objects?",
|
|
"options": [
|
|
"They help express circuit roles and interfaces clearly",
|
|
"They force circuits to be noisy",
|
|
"They replace the need for helper functions",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Register naming is part of diagram legibility and review quality.",
|
|
},
|
|
{
|
|
"prompt": "What does circuit size count in a rough engineering sense?",
|
|
"options": [
|
|
"The number of operations in the circuit",
|
|
"The number of markdown cells in the notebook",
|
|
"Only the number of qubits",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Size is one lens on cost and implementation complexity.",
|
|
},
|
|
{
|
|
"prompt": "What is the risk of copy-paste circuit construction?",
|
|
"options": [
|
|
"Intent drifts silently across duplicated fragments",
|
|
"The simulator stops working",
|
|
"All diagrams become identical",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Duplication is fragile precisely because review becomes harder.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Construction Problem Set B",
|
|
[
|
|
{
|
|
"prompt": "What should a helper block preserve?",
|
|
"options": [
|
|
"A clear local purpose that can still be explained in the larger circuit story",
|
|
"Only a short line count",
|
|
"A guarantee of minimal depth",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Abstractions should compress without becoming opaque.",
|
|
},
|
|
{
|
|
"prompt": "Why compare count previews after refactoring?",
|
|
"options": [
|
|
"To check that the cleaner structure preserved the intended behavior",
|
|
"Because refactoring should always change the output",
|
|
"Because counts replace code review",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Refactoring is only good if intent and behavior survive together.",
|
|
},
|
|
{
|
|
"prompt": "What is an honest use of circuit metrics?",
|
|
"options": [
|
|
"As evidence in a tradeoff discussion",
|
|
"As a replacement for explanation",
|
|
"As proof that one circuit is always superior",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Metrics inform judgment; they do not end it automatically.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Construction Problem Set C",
|
|
[
|
|
{
|
|
"prompt": "What is the best sign that a design is still too flat?",
|
|
"options": [
|
|
"You cannot name any stable substructure worth reusing or reviewing separately",
|
|
"The circuit fits on one screen",
|
|
"It contains only two qubits",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Engineering maturity includes seeing stable blocks inside a larger circuit.",
|
|
},
|
|
{
|
|
"prompt": "Why keep measurement wiring explicit in a builder?",
|
|
"options": [
|
|
"Because the reporting layer is part of the design contract",
|
|
"Because measurements have no relation to evidence",
|
|
"Because builders only exist for classical bits",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Good builders preserve the evidence path, not just the quantum gates.",
|
|
},
|
|
{
|
|
"prompt": "What should happen if an abstraction hides a key design burden?",
|
|
"options": [
|
|
"Refine or split the abstraction so the burden becomes reviewable again",
|
|
"Keep it because shorter code is always better",
|
|
"Remove all helper functions forever",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The point is clarity, not abstraction for its own sake.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Construction Problem Set D",
|
|
[
|
|
{
|
|
"prompt": "What is the strongest beginner-to-engineer transition in this module?",
|
|
"options": [
|
|
"Moving from line-by-line execution toward deliberate block design and metric-aware comparison",
|
|
"Using more qubits",
|
|
"Avoiding diagrams",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "This module is about workflow quality and design control.",
|
|
},
|
|
{
|
|
"prompt": "How should you compare two circuit builders?",
|
|
"options": [
|
|
"Against the same objective, with the same reporting layer, and with explicit cost evidence",
|
|
"Only by code length",
|
|
"Only by how familiar the gate names look",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Fair comparison requires stable objectives and explicit evidence.",
|
|
},
|
|
{
|
|
"prompt": "What belongs in a short design memo for a reusable circuit block?",
|
|
"options": [
|
|
"Intent, interface, cost, and the main tradeoff",
|
|
"Only the final counts",
|
|
"Only the block name",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "A reusable block needs an auditable engineering story.",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
CONSTRUCTION_STUDIO_QUIZ = [
|
|
{
|
|
"prompt": "What distinguishes a studio construction task from a lecture example?",
|
|
"options": [
|
|
"You must choose and defend an abstraction boundary instead of merely reading one",
|
|
"It uses more markdown",
|
|
"It avoids circuit metrics",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The studio asks for design responsibility, not passive recognition.",
|
|
},
|
|
{
|
|
"prompt": "What is the strongest sign that a block earned its place?",
|
|
"options": [
|
|
"Its interface and role stay clear when another engineer reviews it in a larger circuit",
|
|
"It has the shortest possible name",
|
|
"It uses the most gates",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "A good block remains understandable and reusable under review.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_QUIZ_A = [
|
|
{
|
|
"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.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_QUIZ_B = [
|
|
{
|
|
"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.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_LAB_QUIZ_A = [
|
|
{
|
|
"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.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_LAB_QUIZ_B = [
|
|
{
|
|
"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.",
|
|
},
|
|
]
|
|
|
|
|
|
TRANSPILATION_PROBLEM_SETS = [
|
|
(
|
|
"Transpilation Problem Set A",
|
|
[
|
|
{
|
|
"prompt": "What does basis-gate restriction usually force?",
|
|
"options": [
|
|
"Decomposition of higher-level operations into the target gate vocabulary",
|
|
"Automatic noise mitigation",
|
|
"Fewer measurements",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Basis-gate sets are part of the implementation contract.",
|
|
},
|
|
{
|
|
"prompt": "What is the main beginner danger in transpilation work?",
|
|
"options": [
|
|
"Treating the compiled circuit as arbitrary instead of as a response to explicit constraints",
|
|
"Using a coupling map at all",
|
|
"Plotting the compiled diagram",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The compiled rewrite should always be read as a constrained response.",
|
|
},
|
|
{
|
|
"prompt": "Why compare depth before and after compilation?",
|
|
"options": [
|
|
"To quantify part of the cost of fitting the circuit to the target constraints",
|
|
"Because depth determines the statevector size directly",
|
|
"Because depth replaces code review",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Depth is one concrete lens on compile-time inflation.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Transpilation Problem Set B",
|
|
[
|
|
{
|
|
"prompt": "What does a line coupling map make expensive?",
|
|
"options": [
|
|
"Long-range interactions that skip intermediate qubits",
|
|
"Single-qubit gates",
|
|
"Classical registers",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Non-local interactions create routing pressure on a line.",
|
|
},
|
|
{
|
|
"prompt": "Why is a compiled diagram worth discussing with prose?",
|
|
"options": [
|
|
"Because the picture alone does not tell you which cost came from basis conversion and which came from routing",
|
|
"Because prose replaces the diagram",
|
|
"Because the compiled circuit never changes the metrics",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The review story should tie visible changes back to their causes.",
|
|
},
|
|
{
|
|
"prompt": "What is the point of a topology-aware redesign exercise?",
|
|
"options": [
|
|
"To compare compiler rescue with human foresight",
|
|
"To avoid compilation entirely",
|
|
"To force all circuits into identical shapes",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "This is exactly where design judgment starts to matter.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Transpilation Problem Set C",
|
|
[
|
|
{
|
|
"prompt": "What should a good transpilation memo include?",
|
|
"options": [
|
|
"Constraint set, pre/post metrics, and the main structural reason for the rewrite",
|
|
"Only the final counts",
|
|
"Only the number of qubits",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The memo should make the compile-time story auditable.",
|
|
},
|
|
{
|
|
"prompt": "Why is optimization level a teaching lever here?",
|
|
"options": [
|
|
"Because it shows that compilation choices affect the final structure and cost",
|
|
"Because it changes the algorithmic goal itself",
|
|
"Because it disables two-qubit gates",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Optimization level is part of the engineering design space.",
|
|
},
|
|
{
|
|
"prompt": "What is the danger of ignoring the compiled circuit and looking only at counts?",
|
|
"options": [
|
|
"You may miss severe structural inflation that matters for any realistic implementation",
|
|
"Counts already encode the full coupling map",
|
|
"There is no danger if the output looks correct",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Functional correctness is not the whole implementation story.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Transpilation Problem Set D",
|
|
[
|
|
{
|
|
"prompt": "What is the strongest sentence about transpilation maturity?",
|
|
"options": [
|
|
"I can explain what the compiler changed, why it changed it, and what I would redesign upstream",
|
|
"I can run transpile without errors",
|
|
"I can recognize a SWAP symbol",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Maturity means explanatory control and redesign judgment.",
|
|
},
|
|
{
|
|
"prompt": "Which comparison is most useful after compilation?",
|
|
"options": [
|
|
"Abstract intent versus compiled form under stated constraints",
|
|
"Markdown versus code cell counts",
|
|
"Notebook title versus file path",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The entire point is to compare intent with constrained realization.",
|
|
},
|
|
{
|
|
"prompt": "What should happen after you identify avoidable routing pressure?",
|
|
"options": [
|
|
"Propose a topology-aware alternative and compare the metrics honestly",
|
|
"Ignore it because the transpiler handled it",
|
|
"Delete the coupling map",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The engineering response is redesign, not resignation.",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
TRANSPILATION_STUDIO_QUIZ = [
|
|
{
|
|
"prompt": "What is the core job of the transpilation studio?",
|
|
"options": [
|
|
"Compare compiler rescue with human redesign under explicit constraints",
|
|
"Avoid compiled diagrams",
|
|
"Reduce everything to one metric",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The studio asks for engineering judgment, not only observation.",
|
|
},
|
|
{
|
|
"prompt": "When is a redesign claim convincing?",
|
|
"options": [
|
|
"When it states the objective, the constraints, and the evidence for why the new structure is better",
|
|
"When the circuit looks simpler at first glance",
|
|
"When it uses fewer comments",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Convincing redesign requires a reviewable argument.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_QUIZ_A = [
|
|
{
|
|
"prompt": "What is the main difference between a statevector view and a shot-count view?",
|
|
"options": [
|
|
"One describes the ideal pre-measurement object, the other gives sampled evidence from a chosen question",
|
|
"One uses qubits and the other uses classical bits only",
|
|
"They are interchangeable in all contexts",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The right simulator depends on what question you are asking.",
|
|
},
|
|
{
|
|
"prompt": "Why keep an ideal reference before discussing noise?",
|
|
"options": [
|
|
"Because you need a clean story to compare the distortion against",
|
|
"Because noise models cannot be local",
|
|
"Because counts are not allowed without it",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Noise interpretation collapses if the ideal target is already vague.",
|
|
},
|
|
{
|
|
"prompt": "What kind of structural choice often hurts robustness first?",
|
|
"options": [
|
|
"Extra two-qubit depth",
|
|
"Using markdown reflections",
|
|
"Naming registers",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Two-qubit structure is usually the first place local noise hurts visibly.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_QUIZ_B = [
|
|
{
|
|
"prompt": "Why compare ideal counts, sampled counts, and noisy counts in one module?",
|
|
"options": [
|
|
"To separate model, sampling variation, and distortion caused by the noise profile",
|
|
"Because one view is never enough to be valid",
|
|
"Because the noisy view replaces the ideal one",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The three-way comparison clarifies what kind of deviation you are seeing.",
|
|
},
|
|
{
|
|
"prompt": "What is the point of a local demo noise model in this course?",
|
|
"options": [
|
|
"To teach disciplined comparison without requiring cloud access",
|
|
"To imitate every hardware detail exactly",
|
|
"To avoid discussing readout error",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The model is pedagogical and local-first, not a claim of perfect realism.",
|
|
},
|
|
{
|
|
"prompt": "When is a deviation likely structural rather than mere sampling noise?",
|
|
"options": [
|
|
"When it persists at high shot count and lines up with the noise-sensitive parts of the circuit",
|
|
"When a single 64-shot run looks strange",
|
|
"Whenever a histogram is not perfectly balanced",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Persistent, structure-aligned deviations deserve deeper explanation.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_LAB_QUIZ_A = [
|
|
{
|
|
"prompt": "What should you compare first in a noise lab?",
|
|
"options": [
|
|
"The ideal story and the empirical story under the same measurement protocol",
|
|
"Only the noisy plot",
|
|
"Only the circuit title",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Noise reasoning starts from a stable protocol comparison.",
|
|
},
|
|
{
|
|
"prompt": "Why use a wrapper preview with a noise profile in an editable lab?",
|
|
"options": [
|
|
"So the same circuit edits can be inspected under a consistent distortion model",
|
|
"Because noise removes the need for explanation",
|
|
"Because widgets cannot show ideal counts",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The wrapper keeps the experiment local and comparable.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_LAB_QUIZ_B = [
|
|
{
|
|
"prompt": "What does the stressed-circuit comparison teach first?",
|
|
"options": [
|
|
"That small structural changes can create large robustness differences",
|
|
"That noisy simulation is random and therefore useless",
|
|
"That extra entanglers are always good",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Robustness is a design consequence, not merely a hardware complaint.",
|
|
},
|
|
{
|
|
"prompt": "Why keep the measurement protocol fixed when comparing a shallow and a stressed circuit?",
|
|
"options": [
|
|
"So the robustness difference can be attributed to structure rather than to a changed question",
|
|
"Because measurement gates cannot be edited",
|
|
"Because counts always stay identical",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Stable protocol is essential for fair comparison.",
|
|
},
|
|
]
|
|
|
|
|
|
NOISE_PROBLEM_SETS = [
|
|
(
|
|
"Noise Problem Set A",
|
|
[
|
|
{
|
|
"prompt": "What is the cleanest role of a statevector in a noise module?",
|
|
"options": [
|
|
"It supplies the ideal reference description before noisy evidence is considered",
|
|
"It models readout error directly",
|
|
"It replaces the need for shots",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Ideal reference and noisy evidence serve different explanatory roles.",
|
|
},
|
|
{
|
|
"prompt": "Why can a histogram drift even when the circuit did not change?",
|
|
"options": [
|
|
"Finite-shot sampling introduces ordinary variation",
|
|
"The measurement basis changes on its own",
|
|
"The qubit count increases during execution",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Sampling noise should be named before deeper causes are assumed.",
|
|
},
|
|
{
|
|
"prompt": "What does a local noise model help you ask?",
|
|
"options": [
|
|
"How a circuit's structure reacts to a controlled distortion pattern",
|
|
"Which cloud token to request",
|
|
"How to avoid statevectors forever",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The model is for comparison and interpretation, not remote access.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Noise Problem Set B",
|
|
[
|
|
{
|
|
"prompt": "Why are extra two-qubit layers worth stress-testing?",
|
|
"options": [
|
|
"They often increase sensitivity to local noise substantially",
|
|
"They always reduce depth",
|
|
"They remove readout error",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Extra entangling structure is a natural robustness risk.",
|
|
},
|
|
{
|
|
"prompt": "What is a useful three-way comparison in this module?",
|
|
"options": [
|
|
"Ideal probabilities, sampled counts, and noisy counts",
|
|
"Notebook title, file path, and font size",
|
|
"Only low-shot and high-shot counts",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The three-way comparison separates different kinds of deviation.",
|
|
},
|
|
{
|
|
"prompt": "What is the danger of calling every mismatch 'noise' immediately?",
|
|
"options": [
|
|
"You may ignore protocol mistakes or structural design choices that also explain the deviation",
|
|
"There is no danger if the circuit is small",
|
|
"Noise is always the deepest explanation",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Good debugging names ordinary causes before romantic ones.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Noise Problem Set C",
|
|
[
|
|
{
|
|
"prompt": "When does a deviation become more credible as a structural noise effect?",
|
|
"options": [
|
|
"When it tracks the parts of the circuit most exposed to the chosen error model and persists under repeated runs",
|
|
"When it appears once at low shot count",
|
|
"When the compiled circuit looks longer",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Persistent structure-aligned drift deserves a stronger claim.",
|
|
},
|
|
{
|
|
"prompt": "Why compare a lean candidate and a stressed candidate under the same noise profile?",
|
|
"options": [
|
|
"To isolate the effect of structural cost on robustness",
|
|
"Because two candidates are required in every notebook",
|
|
"Because the lean circuit has no statevector",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The comparison is about design consequences, not variety for its own sake.",
|
|
},
|
|
{
|
|
"prompt": "What belongs in a short noise memo?",
|
|
"options": [
|
|
"The ideal target, the observed distortion, and the structural reason you think the gap appeared",
|
|
"Only the backend name",
|
|
"Only the highest bar in the histogram",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "A useful memo ties evidence back to a causal story.",
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"Noise Problem Set D",
|
|
[
|
|
{
|
|
"prompt": "What is the strongest sentence about noise maturity?",
|
|
"options": [
|
|
"I can say which mismatch is probably sampling, which is probably protocol, and which is plausibly structural noise",
|
|
"I can plot a histogram",
|
|
"I know that all real hardware is noisy",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Maturity means discriminating between kinds of failure.",
|
|
},
|
|
{
|
|
"prompt": "Why does this course keep the main noise path local?",
|
|
"options": [
|
|
"Because disciplined interpretation can be trained without cloud dependence",
|
|
"Because local models are always perfectly realistic",
|
|
"Because noise only exists in notebooks",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The goal is clean pedagogy and reproducibility, not false realism claims.",
|
|
},
|
|
{
|
|
"prompt": "What should happen after you identify a noise-sensitive circuit region?",
|
|
"options": [
|
|
"Propose a leaner or better-structured alternative and compare it honestly",
|
|
"Stop using the circuit entirely",
|
|
"Increase the markdown count",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The engineering response is redesign, not resignation.",
|
|
},
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
NOISE_STUDIO_QUIZ = [
|
|
{
|
|
"prompt": "What is the core job of the noise studio?",
|
|
"options": [
|
|
"Compare candidate circuits under the same distortion model and defend a robustness judgment",
|
|
"Avoid ideal references",
|
|
"Treat every histogram difference as proof of hardware failure",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "The studio is about disciplined robustness comparison.",
|
|
},
|
|
{
|
|
"prompt": "What makes a noise design memo convincing?",
|
|
"options": [
|
|
"A stable protocol, explicit evidence, and a structural explanation for the observed gap",
|
|
"Only a large number of shots",
|
|
"Only the prettiest plot",
|
|
],
|
|
"correct_index": 0,
|
|
"explanation": "Good memos tie distortion back to design structure and evidence.",
|
|
},
|
|
]
|
|
|
|
|
|
def build_construction_lecture() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Circuit Construction and Analysis Lecture
|
|
|
|
The foundations band trained you to read circuits as arguments. This module trains you to build them as software artifacts. That shift matters. A professional circuit designer does not only understand a finished diagram. They create circuits that can be reviewed, extended, measured, compared, and reused without turning into a pile of duplicated gate fragments. That is why the first engineering module focuses on construction discipline rather than on new theory.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Learning Objective
|
|
|
|
By the end of this lecture, you should be able to explain why named registers, role separation, reusable blocks, and circuit metrics are not software niceties bolted onto quantum work but part of quantum engineering itself. You should also be able to inspect a small circuit builder and say what the interface is, which substructure is worth encapsulating, and what metrics you would check before trusting the design.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why Construction Discipline Arrives Here
|
|
|
|
Many introductory Qiskit courses keep the learner in a one-cell, one-circuit mindset for too long. That is fine for first exposure but not for professional growth. Once you know what a circuit means, the next question is how to build that meaning repeatedly without drowning in fragile copy-paste code. This is where engineering starts. The quantum content has not vanished. It now lives inside better construction habits: named wires, stable subcircuits, explicit reporting layers, and comparison between candidate implementations.
|
|
|
|
The larger lesson is that software quality and quantum clarity should support each other. If a builder hides the important burden of the circuit, the code is too clever. If a circuit can only be understood by re-reading every line from scratch, the structure is too flat. Good construction creates reviewable shapes.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Registers Are Interfaces, Not Storage Buckets
|
|
|
|
A register in Qiskit is more than a container for qubits or classical bits. In an engineering notebook it is often the first place where intent becomes explicit. If a circuit has data wires, a check wire, and a readout register, those names tell the next reader what role each part is supposed to play. That matters for review because many errors are really interface errors: a measurement landed in the wrong classical slot, an ancilla-like wire got reused as payload, or a builder silently changed the reporting contract.
|
|
|
|
Naming registers does not solve every problem, but it pushes the circuit toward role-bearing structure instead of anonymous positions. This is one of the earliest places where the field starts resembling real software architecture rather than one-off experiments.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Blocks, Builders, And Abstraction Boundaries
|
|
|
|
The next leap is block thinking. A good builder takes a recurring substory and gives it a stable interface. That might be a preparation block, a measurement block, or a parity-style check layer. The point is not to worship abstraction. The point is to decide what deserves to be isolated and what should remain visible. That decision is where engineering judgment begins.
|
|
|
|
There is a real risk here. If you abstract too aggressively, the circuit becomes shorter but less reviewable. If you never abstract, every change becomes brittle and repetitive. The right balance is to isolate stable substructure while keeping the causal story of the whole circuit easy to narrate. This lecture keeps returning to that tradeoff because it will remain important through algorithms, hardware-aware redesign, and capstone work.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Metrics Are Evidence, Not Decorations
|
|
|
|
Once circuits become software artifacts, metrics matter. Depth, size, width, and gate counts are not the whole story, but they are part of the story. They tell you something about cost, review surface, compilation pressure, and later noise sensitivity. The mistake is not using metrics. The mistake is using them without linking them back to the design move that caused them.
|
|
|
|
In this course, a metric is useful only when you can pair it with a sentence such as "depth increased because I added a second entangling layer" or "size stayed stable after refactoring, which suggests the builder improved structure without altering behavior." Metrics without explanation are trivia. Explanation without metrics is often too vague. Good engineering uses both.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Code-To-Diagram Anchor
|
|
|
|
The anchor circuit below is intentionally modest. The point is not algorithmic novelty. The point is to show how naming, role separation, and explicit reporting create a cleaner engineering object. Read the marker table first, then inspect the editable builder.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({CONSTRUCTION_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
CONSTRUCTION_ANCHOR,
|
|
title="Construction Anchor: Named Registers And Roles",
|
|
instructions="Change one role-bearing choice at a time: a register name, a parity path, or the reporting mapping. Explain what that change does to the circuit story and the reviewability.",
|
|
),
|
|
code_cell(
|
|
"""
|
|
data = QuantumRegister(2, "data")
|
|
syndrome = QuantumRegister(1, "syndrome")
|
|
readout = ClassicalRegister(3, "readout")
|
|
anchor = QuantumCircuit(data, syndrome, readout)
|
|
anchor.h(data[0])
|
|
anchor.cx(data[0], data[1])
|
|
anchor.cx(data[0], syndrome[0])
|
|
anchor.cx(data[1], syndrome[0])
|
|
anchor.measure(data[0], readout[0])
|
|
anchor.measure(data[1], readout[1])
|
|
anchor.measure(syndrome[0], readout[2])
|
|
|
|
{
|
|
"depth": anchor.depth(),
|
|
"size": anchor.size(),
|
|
"width": anchor.width(),
|
|
"ops": dict(anchor.count_ops()),
|
|
}
|
|
"""
|
|
),
|
|
quiz_code(CONSTRUCTION_QUIZ_A, "Construction Checkpoint A"),
|
|
markdown_cell(
|
|
"""
|
|
## What A Good Builder Tries To Protect
|
|
|
|
A good builder protects three things at once. First, it protects intent: another reader can still tell what the circuit is for. Second, it protects behavior: refactoring does not silently change the experiment. Third, it protects evidence: the readout and reporting contract remain clear. That three-part standard is useful because it prevents the common mistake of treating refactoring as purely a software cleanup exercise. In a notebook-first quantum project, refactoring is part of scientific and engineering clarity too.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Construction Review Questions
|
|
|
|
When you look at a circuit builder, ask these questions. Which wires carry which roles? What substructure repeats enough to deserve a helper? What information would be lost if I hid this region behind a function call? Which metric would move first if I added another layer here? And how is the classical reporting path being protected? Those are the kinds of questions that turn "I can write Qiskit code" into "I can review and evolve circuit software."
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## From Notebook Cell To Maintainable Asset
|
|
|
|
A useful mental shift in this module is to stop asking only whether a circuit cell is correct today and start asking whether the same circuit would still be clear next week, after one more feature, or in the hands of another engineer. That is what makes this an engineering module instead of only a syntax module. Code that merely works in the current cell can still fail as a maintainable artifact if it mixes roles, hides the reporting contract, or repeats the same structural idea in slightly different forms.
|
|
|
|
In practice, maintainability comes from a small number of disciplined habits. Name the wires by role when role matters. Separate stable preparation structure from experiment-specific reporting structure. Encapsulate repeated logic, but only where the encapsulation leaves the burden of the circuit intelligible. Keep at least one metric in view so the cost of a structural choice is not left to vague intuition. And, most importantly, write enough explanation around the builder that another reader could tell whether a refactor preserved the scientific intention or merely preserved that the cell still executes.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Review Heuristics That Scale Later
|
|
|
|
The heuristics installed here will keep reappearing in later bands. If an algorithm notebook gets too tangled, you will still ask which substructure deserves its own builder. If a transpiled circuit looks expensive, you will still ask which abstract region created the burden. If a noisy result looks unstable, you will still ask whether the reporting layer is clear enough that the evidence is even being read correctly. This is why the current examples stay small. The aim is not to impress you with size. The aim is to make these review heuristics visible before later modules make them harder to see.
|
|
"""
|
|
),
|
|
quiz_code(CONSTRUCTION_QUIZ_B, "Construction Checkpoint B"),
|
|
reflection_code(
|
|
"Write a short paragraph defending one abstraction boundary in the anchor circuit and one region you would deliberately keep inline."
|
|
),
|
|
reflection_code(
|
|
"Name one metric you would track while refactoring a circuit builder and explain what design move you think that metric is proxying for."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Mastery Gate
|
|
|
|
You should leave this lecture able to build a small circuit with explicit roles, explain why a block boundary exists, and use a circuit metric as evidence in a design discussion instead of as isolated trivia.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_construction_lab() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Circuit Construction and Analysis Lab
|
|
|
|
The lab converts engineering vocabulary into controlled edits. The question is not whether the code runs. The question is whether you can change interface, structure, and abstraction while keeping the intended behavior and the reporting contract legible.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Protocol
|
|
|
|
Predict what should stay stable before you edit anything. Then change one structural choice at a time. After each render, inspect the diagram, the preview counts, and the circuit metrics you care about. The discipline here is not only quantum. It is also software engineering discipline applied to a quantum artifact.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 1: Role-Bearing Registers
|
|
|
|
Start with the named-register anchor. Change one interface-level choice and ask whether the change improved or degraded reviewability. This is the fastest way to stop treating register declarations as boilerplate.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({CONSTRUCTION_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
CONSTRUCTION_ANCHOR,
|
|
title="Lab 1: Register Roles",
|
|
instructions="Rename a register, alter the check wire, or change the reporting map. Then explain what became clearer or more fragile.",
|
|
),
|
|
quiz_code(CONSTRUCTION_LAB_QUIZ_A, "Construction Lab Checkpoint A"),
|
|
reflection_code(
|
|
"Which register-level edit most changed how readable the circuit felt, and why?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 2: Builder Refactor
|
|
|
|
Now compare inline construction with block composition. The purpose is not to prove that helpers are always better. The purpose is to decide whether a helper actually preserved the circuit story while removing fragile duplication.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
CONSTRUCTION_BLOCK_EDITABLE,
|
|
title="Lab 2: Builder Refactor",
|
|
instructions="Split or merge the helper blocks and explain whether the resulting abstraction boundary improved the design.",
|
|
),
|
|
code_cell(
|
|
"""
|
|
flat = QuantumCircuit(2, 2)
|
|
flat.h(0)
|
|
flat.cx(0, 1)
|
|
flat.measure([0, 1], [0, 1])
|
|
|
|
def bell_block() -> QuantumCircuit:
|
|
block = QuantumCircuit(2, name="bell_prep")
|
|
block.h(0)
|
|
block.cx(0, 1)
|
|
return block
|
|
|
|
structured = QuantumCircuit(2, 2)
|
|
structured.compose(bell_block(), inplace=True)
|
|
structured.measure([0, 1], [0, 1])
|
|
|
|
{
|
|
"flat_metrics": {"depth": flat.depth(), "size": flat.size(), "ops": dict(flat.count_ops())},
|
|
"structured_metrics": {"depth": structured.depth(), "size": structured.size(), "ops": dict(structured.count_ops())},
|
|
}
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
The important lesson in this lab is that a cleaner code shape is only valuable if the circuit story and interface remain visible. Refactoring is not a beauty contest. It is a reviewability contest.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 3: Metric-Aware Candidate Comparison
|
|
|
|
Use the next widget to compare two structural candidates for the same reporting task. Toggle the extra entangling layer and inspect what the metrics and preview counts say together.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
CONSTRUCTION_METRICS_EDITABLE,
|
|
title="Lab 3: Candidate Comparison",
|
|
instructions="Compare the lean and extra-layer candidates. Keep the reporting contract stable and explain which structural cost is being paid.",
|
|
),
|
|
code_cell(
|
|
"""
|
|
def candidate(extra_layer: bool) -> QuantumCircuit:
|
|
circuit = QuantumCircuit(3, 3)
|
|
circuit.h(0)
|
|
circuit.cx(0, 1)
|
|
circuit.cx(1, 2)
|
|
if extra_layer:
|
|
circuit.cx(0, 2)
|
|
circuit.measure([0, 1, 2], [0, 1, 2])
|
|
return circuit
|
|
|
|
lean = candidate(False)
|
|
stressed = candidate(True)
|
|
|
|
{
|
|
"lean": {"depth": lean.depth(), "size": lean.size(), "ops": dict(lean.count_ops())},
|
|
"stressed": {"depth": stressed.depth(), "size": stressed.size(), "ops": dict(stressed.count_ops())},
|
|
}
|
|
"""
|
|
),
|
|
quiz_code(CONSTRUCTION_LAB_QUIZ_B, "Construction Lab Checkpoint B"),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Debrief
|
|
|
|
The three labs in this module are rehearsing the same engineering claim from different angles: circuit quality is not only about correct output. It is also about whether the interface is legible, whether the repeated structure is reviewable, and whether the cost of a design choice is visible in the metrics. That is why the lab asks you to keep the reporting layer stable while changing the builders. Stable objective plus controlled structural edits is the only way to tell whether your engineering explanation is getting sharper.
|
|
|
|
If these exercises feel slower than ordinary tutorial work, that is appropriate. Tutorials often reward motion. Engineering education has to reward discrimination. The point is to notice which edits are merely cosmetic, which edits improve reviewability, and which edits quietly create future maintenance cost. Those distinctions become expensive later if they are not practiced now.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Write a short note explaining which candidate you would keep and what evidence actually supports that choice."
|
|
),
|
|
reflection_code(
|
|
"Describe one case where a helper function could make a circuit harder, not easier, to review."
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_construction_problems() -> dict:
|
|
cells = [
|
|
markdown_cell(
|
|
"""
|
|
# Circuit Construction and Analysis Problems
|
|
|
|
This notebook checks whether construction vocabulary is becoming engineering judgment. The questions are not about syntax recall alone. They are about identifying interface clarity, abstraction quality, and metric use in short, review-like situations.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## How To Use This Notebook
|
|
|
|
Answer each quiz slowly enough that you can explain why the wrong options remain tempting. Many weak engineering habits survive because they sound efficient. This notebook is trying to make those weak habits explicit enough to reject.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
]
|
|
|
|
for heading, questions in CONSTRUCTION_PROBLEM_SETS:
|
|
cells.append(
|
|
markdown_cell(
|
|
f"""
|
|
## {heading}
|
|
|
|
Treat these as miniature code-review prompts. Each answer should sharpen the sentence you would later use in a design memo or review comment.
|
|
"""
|
|
)
|
|
)
|
|
cells.append(quiz_code(questions, heading))
|
|
|
|
cells.extend(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
## Mini Case: Shorter Code, Worse Design
|
|
|
|
One of the easiest engineering traps to fall into is mistaking shorter code for better design. A helper that compresses six lines into two can still be a worse artifact if it hides the measurement contract, muddles wire roles, or forces the next reader to reverse engineer the meaning from scratch. This module pushes against that trap repeatedly because it is common in notebook-heavy projects. Notebook users often optimize for immediate convenience, but a learning platform that aims at professional skill has to optimize for reviewable structure instead.
|
|
|
|
Keep that case in mind while answering the quizzes above. Whenever an option sounds efficient, ask efficient for whom and efficient under what future change. That is a stronger engineering question than "is the current cell shorter."
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Written Checks
|
|
|
|
Use the prompts below to rehearse complete engineering sentences rather than isolated labels.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Explain why explicit register roles and explicit reporting layers belong in the same engineering conversation."
|
|
),
|
|
reflection_code(
|
|
"Write a short design-review note critiquing a circuit builder that is shorter but less narratable than the inline version."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Exit Condition
|
|
|
|
Move on when you can justify a builder shape, not only write one. This module is about deliberate construction, not just circuit-producing syntax.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
return notebook(cells)
|
|
|
|
|
|
def build_construction_studio() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Circuit Construction and Analysis Studio
|
|
|
|
The studio asks you to design small, reviewable circuit software. The focus is not novelty. The focus is whether your builders expose role structure, preserve behavior, and support comparison between candidates.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Standard
|
|
|
|
Every design brief in this notebook should end with a short memo: what the builder is for, what the interface contract is, and what tradeoff you chose between flat visibility and reusable structure.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 1: Small Reusable Preparation Block
|
|
|
|
Start from the block-based Bell example and redesign it so another engineer could reuse it safely. Your goal is to make the interface and role of the block obvious.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
CONSTRUCTION_BLOCK_EDITABLE,
|
|
title="Studio Brief 1: Reusable Preparation Block",
|
|
instructions="Refine the block boundary and defend why it is the right amount of abstraction for this task.",
|
|
),
|
|
reflection_code(
|
|
"What is the contract of your block, and what design burden did you intentionally leave outside the helper?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 2: Register-Structured Multiwire Design
|
|
|
|
Use the named-register anchor as a starting point and redesign the role structure if needed. The point is to express interface clearly, not to maximize the number of wires.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
CONSTRUCTION_ANCHOR,
|
|
title="Studio Brief 2: Register Structure",
|
|
instructions="Reassign or rename circuit roles and then defend why the new interface is more reviewable.",
|
|
),
|
|
reflection_code(
|
|
"Which register name or split carries the most explanatory value in your design?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 3: Candidate Comparison Memo
|
|
|
|
Use the metric-aware candidate constructor and decide which version you would keep in a real codebase. Your answer should cite both structural reasoning and evidence.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
CONSTRUCTION_METRICS_EDITABLE,
|
|
title="Studio Brief 3: Candidate Comparison Memo",
|
|
instructions="Toggle the extra layer, inspect the cost, and write the memo you would leave in a review.",
|
|
),
|
|
quiz_code(CONSTRUCTION_STUDIO_QUIZ, "Construction Studio Check"),
|
|
reflection_code(
|
|
"What is the biggest construction smell you can now recognize in your own notebook code?"
|
|
),
|
|
reflection_code(
|
|
"Which abstraction decision in this studio still feels uncertain, and what extra evidence would settle it?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Rubric
|
|
|
|
Judge your studio work with four questions. First, does the builder expose the roles of the wires clearly enough that another engineer could review the circuit without re-reading every line? Second, is the abstraction boundary justified by stable structure rather than by a vague desire for shorter code? Third, did you keep the reporting layer explicit enough that behavior can be checked after refactoring? Fourth, did you use at least one metric as evidence for a tradeoff rather than as decorative data?
|
|
|
|
That rubric matters because the studio is where circuit code stops being a private exercise and starts becoming something like a shared engineering artifact. A builder that survives this rubric on a tiny example is much more likely to survive the later algorithmic and hardware-aware bands.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Exit
|
|
|
|
A good outcome is a small circuit design that another engineer could extend without re-learning the whole artifact from scratch. That is the engineering standard this module is trying to install.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_transpilation_lecture() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Transpilation and Visualization Lecture
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Learning Objective
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why This Band Leaves The Ideal World
|
|
|
|
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.
|
|
|
|
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."
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## The Three Main Sources Of Compile-Time Cost
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Visualization Is Part Of The Analysis
|
|
|
|
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.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Code-To-Diagram Anchor
|
|
|
|
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.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({TRANSPILATION_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
TRANSPILATION_ABSTRACT_EDITABLE,
|
|
title="Transpilation Anchor: Abstract Circuit",
|
|
instructions="Edit the abstract circuit first. Ask which line is likely to trigger routing pressure on a four-qubit line.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
code_cell(
|
|
"""
|
|
defaults = load_experiment_defaults()
|
|
|
|
abstract = QuantumCircuit(4, 4)
|
|
abstract.h(0)
|
|
abstract.cx(0, 3)
|
|
abstract.cx(3, 1)
|
|
abstract.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
|
|
|
summary = transpile_summary(
|
|
abstract,
|
|
BasicSimulator(),
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(4),
|
|
optimization_level=0,
|
|
)
|
|
|
|
{
|
|
"depth_before": summary["depth_before"],
|
|
"depth_after": summary["depth_after"],
|
|
"size_before": summary["size_before"],
|
|
"size_after": summary["size_after"],
|
|
"ops_before": summary["ops_before"],
|
|
"ops_after": summary["ops_after"],
|
|
}
|
|
"""
|
|
),
|
|
code_cell("draw_circuit(summary['compiled_circuit'])"),
|
|
quiz_code(TRANSPILATION_QUIZ_A, "Transpilation Checkpoint A"),
|
|
markdown_cell(
|
|
"""
|
|
## Reading The Rewrite
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## When Human Redesign Beats Compiler Rescue
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## A Practical Reading Order For Compiled Circuits
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why This Module Uses Local Constraints Instead Of Cloud Hardware
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Three Common Compile-Time Failure Patterns
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## How To Write A Useful Transpilation Memo
|
|
|
|
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?
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Case Study Thinking Instead Of Feature Spotting
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why Design Accountability Still Belongs To The Human
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
quiz_code(TRANSPILATION_QUIZ_B, "Transpilation Checkpoint B"),
|
|
reflection_code(
|
|
"Write a short paragraph explaining the difference between an abstract circuit being logically clear and the same circuit being implementation-friendly."
|
|
),
|
|
reflection_code(
|
|
"Name one sign in a compiled circuit that would make you consider an upstream redesign instead of only accepting the transpiler output."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Mastery Gate
|
|
|
|
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.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_transpilation_lab() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Transpilation and Visualization Lab
|
|
|
|
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.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Protocol
|
|
|
|
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.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 1: Abstract Pressure
|
|
|
|
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.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({TRANSPILATION_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
TRANSPILATION_ABSTRACT_EDITABLE,
|
|
title="Lab 1: Abstract Pressure",
|
|
instructions="Move the long-range interaction or replace it with a local chain. Then explain how that should affect the compiled cost.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
quiz_code(TRANSPILATION_LAB_QUIZ_A, "Transpilation Lab Checkpoint A"),
|
|
reflection_code(
|
|
"Which abstract line in the circuit is carrying the main routing burden, and how do you know?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 2: Render The Compiled Form Directly
|
|
|
|
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.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
TRANSPILATION_COMPILED_EDITABLE,
|
|
title="Lab 2: Compiled Circuit View",
|
|
instructions="Edit the abstract source or the optimization level in the code and inspect how the compiled diagram and metrics change.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
code_cell(
|
|
"""
|
|
defaults = load_experiment_defaults()
|
|
abstract = QuantumCircuit(4, 4)
|
|
abstract.h(0)
|
|
abstract.cx(0, 3)
|
|
abstract.cx(3, 1)
|
|
abstract.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
|
|
|
opt0 = transpile_summary(
|
|
abstract,
|
|
BasicSimulator(),
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(4),
|
|
optimization_level=0,
|
|
)
|
|
opt3 = transpile_summary(
|
|
abstract,
|
|
BasicSimulator(),
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(4),
|
|
optimization_level=3,
|
|
)
|
|
|
|
{
|
|
"opt0_depth_after": opt0["depth_after"],
|
|
"opt3_depth_after": opt3["depth_after"],
|
|
"opt0_ops_after": opt0["ops_after"],
|
|
"opt3_ops_after": opt3["ops_after"],
|
|
}
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 3: Topology-Aware Rewrite
|
|
|
|
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.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
TRANSPILATION_TOPOLOGY_AWARE_EDITABLE,
|
|
title="Lab 3: Topology-Aware Rewrite",
|
|
instructions="Keep the measurement protocol stable while you compare the local-chain rewrite to the topology-hostile original.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
quiz_code(TRANSPILATION_LAB_QUIZ_B, "Transpilation Lab Checkpoint B"),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Debrief
|
|
|
|
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.
|
|
|
|
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.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Write a short memo explaining when the topology-aware rewrite is genuinely better and when it might overfit to one target."
|
|
),
|
|
reflection_code(
|
|
"Describe one compiled-circuit feature that you can now interpret causally instead of treating as arbitrary clutter."
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_transpilation_problems() -> dict:
|
|
cells = [
|
|
markdown_cell(
|
|
"""
|
|
# Transpilation and Visualization Problems
|
|
|
|
This notebook checks whether compile-time thinking is becoming explicit. The questions focus on constraints, routing pressure, metric interpretation, and redesign judgment.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## How To Use This Notebook
|
|
|
|
Answer as if you were preparing short review comments for another engineer. The goal is not only to know facts about transpilation but to speak about compile-time consequences precisely.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
]
|
|
|
|
for heading, questions in TRANSPILATION_PROBLEM_SETS:
|
|
cells.append(
|
|
markdown_cell(
|
|
f"""
|
|
## {heading}
|
|
|
|
Treat these quizzes as compact design-review drills. You are practicing the sentences that make compiled circuits intelligible.
|
|
"""
|
|
)
|
|
)
|
|
cells.append(quiz_code(questions, heading))
|
|
|
|
cells.extend(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
## Mini Case: Correct Output, Expensive Realization
|
|
|
|
One of the central lessons of this module is that functional correctness does not end the engineering discussion. Two circuits can implement the same logical objective and even produce the same sampled evidence while having very different compile-time cost. If you ignore that, you will think the transpiler is only a performance detail. It is not. It is part of the design story.
|
|
|
|
The quizzes above are therefore not only about definitions. They are practice in keeping implementation cost visible even when the logical story stayed stable. That distinction is one of the main differences between being able to run circuits and being able to engineer them.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Mini Case: Same Intent, Better Topology Cooperation
|
|
|
|
A useful transpilation exercise is to imagine two teammates proposing circuits for the same local objective. One writes the conceptually shortest abstract circuit and leaves the routing burden to the compiler. The other writes a slightly more topology-aware version that is less elegant at first glance but compiles more gently. Which one is better? The only serious answer is: compare them under the same constraints and state the tradeoff honestly.
|
|
|
|
This mini case is here to reinforce that transpilation reasoning is not about punishing abstraction. It is about deciding when abstract elegance and physical cooperation pull in different directions and writing down that tension clearly.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Written Checks
|
|
|
|
Use the prompts below to rehearse the core explanatory loop of this module: state the constraints, describe the rewrite, and justify the redesign recommendation.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Explain why the same logical circuit can have very different compiled forms under different topologies or optimization levels."
|
|
),
|
|
reflection_code(
|
|
"Write a short review note on when accepting the transpiler output is good enough and when upstream redesign is the stronger choice."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Exit Condition
|
|
|
|
Move on only when you can read a compiled circuit as a constrained implementation, not as unexplained visual clutter.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
return notebook(cells)
|
|
|
|
|
|
def build_transpilation_studio() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Transpilation and Visualization Studio
|
|
|
|
The studio asks you to act like a hardware-aware reviewer. You will compare hostile and cooperative designs under explicit constraints and write the kind of redesign memo that later bands will depend on.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Standard
|
|
|
|
Every answer should state the abstract objective, the constraint set, and the evidence for why one implementation path is better than another. If any one of those is missing, the redesign claim is too soft.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 1: Rescue Versus Redesign
|
|
|
|
Start from the topology-hostile abstract circuit and write the memo you would leave after seeing the compiled result.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
TRANSPILATION_COMPILED_EDITABLE,
|
|
title="Studio Brief 1: Rescue Versus Redesign",
|
|
instructions="Inspect the compiled form and decide whether you would accept the rescue or redesign the abstract circuit.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
reflection_code(
|
|
"What exactly did the compiler have to rescue, and what upstream change would reduce that burden?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 2: Cooperative Rewrite
|
|
|
|
Build the topology-aware version and compare it honestly to the compiler-rescued one.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
TRANSPILATION_TOPOLOGY_AWARE_EDITABLE,
|
|
title="Studio Brief 2: Cooperative Rewrite",
|
|
instructions="Tune the local-chain design and explain whether it preserves the objective while reducing compile-time pain.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
reflection_code(
|
|
"What did your redesign gain, and what did it potentially give up?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 3: Optimization-Level Judgment
|
|
|
|
Use the compiled-render widget again, but this time focus on optimization-level tradeoffs and the review language they require.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
TRANSPILATION_COMPILED_EDITABLE,
|
|
title="Studio Brief 3: Optimization Judgment",
|
|
instructions="Adjust the optimization level or the abstract structure and defend the compiled result you would ship for this lesson's local target.",
|
|
context_source="""
|
|
defaults = load_experiment_defaults()
|
|
constrained_counts = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
basis_gates=list(defaults.transpile.basis_gates),
|
|
coupling_map=line_coupling_map(circuit.num_qubits),
|
|
)
|
|
""",
|
|
context_name="constrained_counts",
|
|
),
|
|
quiz_code(TRANSPILATION_STUDIO_QUIZ, "Transpilation Studio Check"),
|
|
reflection_code(
|
|
"What is the main transpilation smell you can now recognize in an abstract circuit before compiling it?"
|
|
),
|
|
reflection_code(
|
|
"Which redesign judgment in this module still feels tentative, and what extra comparison would strengthen it?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Rubric
|
|
|
|
Use a strict rubric here. A strong studio answer states the objective without ambiguity, states the constraint set explicitly, shows the relevant pre- and post-compile evidence, and gives a redesign recommendation that another engineer could actually test. A weak answer says only that one circuit "looks cleaner" or "seems more hardware aware." The studio exists to eliminate that kind of soft language.
|
|
|
|
This rubric matters because later hardware-aware modules will assume you can already argue about compile-time tradeoffs in review language. The current studio is where that language gets installed.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Review Standard
|
|
|
|
Before you leave this notebook, ask whether your memo would still make sense to a reader who never saw your intermediate experiments. If the answer is no, then the memo is still too dependent on the notebook context. A good engineering memo should survive outside the cell stream. It should say enough about the objective, constraints, and cost story that another person could reproduce the comparison or challenge your conclusion intelligently.
|
|
|
|
That standard is intentionally high. The whole point of the studio is to move from personal experimentation toward shared engineering language.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Exit
|
|
|
|
A strong outcome is a short redesign memo that another engineer could act on: it states the target, the constraints, the cost, and the reason the chosen rewrite is preferable.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_noise_lecture() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Simulation and Noise Models Lecture
|
|
|
|
This module teaches the second half of the ideal-versus-reality split. The previous module showed how compilation distorts clean intent into constrained implementation. This one shows how evidence itself changes once sampling variation and local error enter the picture. A professional circuit designer needs a disciplined way to compare ideal models, shot-based evidence, and noisy distortion without collapsing them into one blurry story.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Learning Objective
|
|
|
|
By the end of this lecture, you should be able to choose an appropriate local simulator for the question being asked, explain why ideal probabilities and sampled counts differ in status, build a simple local noise profile, and state which mismatches are likely structural rather than mere shot noise.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why Simulator Choice Matters
|
|
|
|
A statevector is the right tool when you want the ideal pre-measurement description. Shot-based simulation is the right tool when you care about the evidence produced by repeated measurement under a fixed protocol. A local noise model is the right tool when you want to ask how a specific distortion pattern interacts with the circuit structure. Confusion begins when these lenses are used interchangeably. Good engineering begins when the lens matches the question.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Ideal, Sampled, And Noisy Are Three Different Claims
|
|
|
|
The ideal probability model is a claim about what the circuit would do in the clean reference story. The shot-based histogram is a claim about what repeated sampling of that story produced in one finite run. The noisy histogram is a claim about what happens after a distortion model has been applied on top of the compiled circuit. Those are three different statements. A good notebook keeps them separate long enough to compare them honestly.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Structural Vulnerability
|
|
|
|
Not every circuit pays the same price under noise. Extra two-qubit depth, wider entangling structure, and poorly motivated additional layers can all make the empirical distribution drift farther from the clean reference. This is one reason engineering quality matters upstream. A leaner circuit is often not only cleaner to read and easier to compile. It may also be more robust under the same local distortion profile.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Code-To-Diagram Anchor
|
|
|
|
The anchor keeps the circuit simple so the comparison between ideal, sampled, and noisy views remains easy to narrate. Read the marker table first, then inspect the editable version.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({NOISE_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
NOISE_IDEAL_EDITABLE,
|
|
title="Noise Anchor: Ideal Reference Circuit",
|
|
instructions="Keep the ideal story clear. Change only one structural choice at a time and compare how the ideal and sampled stories respond.",
|
|
),
|
|
code_cell(
|
|
"""
|
|
anchor = QuantumCircuit(2, 2)
|
|
anchor.h(0)
|
|
anchor.cx(0, 1)
|
|
anchor.measure([0, 1], [0, 1])
|
|
|
|
ideal_probs = statevector_probabilities(anchor)
|
|
sampled_counts = simulate_counts(anchor, shots=512)
|
|
noisy_counts = simulate_counts(
|
|
anchor,
|
|
shots=512,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
|
|
{
|
|
"ideal_probabilities": ideal_probs,
|
|
"sampled_probabilities": counts_to_probabilities(sampled_counts),
|
|
"noisy_probabilities": counts_to_probabilities(noisy_counts),
|
|
}
|
|
"""
|
|
),
|
|
quiz_code(NOISE_QUIZ_A, "Noise Checkpoint A"),
|
|
markdown_cell(
|
|
"""
|
|
## What A Local Noise Model Is And Is Not
|
|
|
|
In this platform the local noise model is a teaching instrument. It is not a claim that we have reconstructed a specific piece of hardware in microscopic detail. That would be the wrong promise for a local-first course. The model is valuable because it lets you ask disciplined what-if questions locally: what if two-qubit operations are appreciably noisier than single-qubit ones, what if readout introduces a small but persistent bias, and how does a deeper circuit compare to a leaner one under the same assumptions?
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## From Drift To Diagnosis
|
|
|
|
A mismatch between ideal and empirical behavior is only useful if you can classify it. Is it ordinary finite-shot variation? Is it a protocol mistake, such as a changed basis or reporting map? Is it a structural sensitivity to the chosen noise profile? This module is trying to build exactly that classification habit. Without it, noisy evidence becomes a swamp of vaguely disappointing histograms.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## A Clean Diagnostic Order
|
|
|
|
Use a stable diagnostic order when results drift. First, confirm the protocol: basis, wiring, and shot count. Second, inspect the ideal reference so you know what target you are actually comparing against. Third, ask whether the mismatch could be ordinary sampling variation. Only after those checks should you lean into structural noise explanations. This order matters because it prevents the all-purpose excuse of saying "hardware is noisy" before easier explanations have been ruled out.
|
|
|
|
The value of a local-first course is that you can rehearse this order without any cloud dependence. The notebook can generate clean ideal evidence, sampled evidence, and locally distorted evidence side by side. That makes the classification habit much easier to train than if every question had to start with a remote backend run.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why Robustness Is A Design Property
|
|
|
|
It is tempting to treat robustness as something that hardware either grants or withholds. That is not the whole story. Circuit structure helps determine how gracefully a design will degrade. Extra entangling depth, repeated two-qubit burden, and avoidable structural clutter can all make a circuit a worse experimental object under the same local assumptions. This is why robustness belongs in the design conversation, not only in the postmortem.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## The Difference Between Sampling Drift And Structural Drift
|
|
|
|
Sampling drift is ordinary statistical variation around the same underlying probability story. Structural drift is different. It means the observed distribution is being pulled away from the ideal reference in a way that tracks some aspect of the circuit design or the applied noise model. In practice, the distinction is visible through repetition and alignment. If the same kind of distortion persists at higher shot counts and appears strongest where the circuit is structurally heaviest, that is a clue that the drift is not just statistical noise.
|
|
|
|
This distinction matters because it changes the engineering response. Sampling drift calls for shot reasoning and restraint. Structural drift calls for design analysis and possibly redesign. A serious learning platform has to make that fork explicit early.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Limits Of A Local Demo Noise Model
|
|
|
|
The local model in this course is intentionally simple. That simplicity is a feature for teaching, but it also creates limits that should be named honestly. The model cannot capture every temporal correlation, calibration detail, or device-specific quirk of a real backend. What it can do is expose the broad fact that some structures are more distortion-sensitive than others and that those sensitivities can be compared under controlled local assumptions.
|
|
|
|
This honesty matters pedagogically. If the learner mistakes the local model for perfect realism, they will draw the wrong lesson. The right lesson is subtler and more useful: even a simplified model is enough to train good comparison habits and to show that robustness is partly a property of circuit structure, not only of mysterious hardware fate.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## How To Write A Useful Noise Memo
|
|
|
|
A useful noise memo does not begin with disappointment. It begins with a clean comparison structure. State the ideal target first. State the measurement protocol second. State the local noise assumptions third. Only then describe the observed gap. After that, propose the most plausible explanation categories in order: sampling variation, protocol mistake, structural sensitivity, or some combination. Finally, suggest the next discriminating check. This memo format matters because it turns a noisy plot from a vague mood into an engineering document.
|
|
|
|
The reason to practice this memo style so early is that later modules will depend on it heavily. Verification work, hardware-aware debugging, and capstone review all need concise ways to say what changed, what stayed fixed, and what still remains uncertain. The noise memo is one of the first places where disciplined uncertainty becomes part of the craft.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Why This Module Comes Before Real Hardware Dependence
|
|
|
|
Some learners assume that serious noise thinking has to wait until they are using live cloud hardware. That is backwards. What has to come first is the ability to compare a clean model, a sampled run, and a controlled distortion pattern without losing the explanatory thread. If that skill is missing, real hardware access mostly adds confusion. Local-first noisy simulation is therefore not a substitute for seriousness. It is the training ground that makes later seriousness possible.
|
|
|
|
This sequencing choice also protects the beginner from the wrong kind of complexity. The point right now is not to capture every calibration detail. The point is to learn how circuit structure, error assumptions, and measurement evidence interact. Once that triangle is clear, richer hardware data becomes a refinement instead of an avalanche.
|
|
"""
|
|
),
|
|
quiz_code(NOISE_QUIZ_B, "Noise Checkpoint B"),
|
|
reflection_code(
|
|
"Explain why the same circuit should usually be inspected through all three lenses: ideal model, sampled evidence, and noisy evidence."
|
|
),
|
|
reflection_code(
|
|
"Name one structural reason a circuit might drift more under noise even when the logical objective stayed the same."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Mastery Gate
|
|
|
|
You should leave this lecture able to say which simulator or view matches which question, and to explain a mismatch without immediately collapsing every deviation into the single word noise.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_noise_lab() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Simulation and Noise Models Lab
|
|
|
|
The lab puts the three-view comparison into motion. You will keep the protocol stable, change the structural burden of the circuit, and compare ideal, sampled, and noisy evidence under a consistent local model.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Protocol
|
|
|
|
Predict before each run which differences should count as ordinary sampling variation and which should count as structural vulnerability. Then use the same measurement protocol for fair comparison. The lab is about disciplined attribution, not just plot collection.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 1: Ideal And Sampled
|
|
|
|
Start with the clean Bell-style reference. This lab is only about separating the ideal model from finite-shot evidence before noise is added.
|
|
"""
|
|
),
|
|
code_cell(f"step_reference_table({NOISE_STEP_REFS!r})"),
|
|
editable_lab_code(
|
|
NOISE_IDEAL_EDITABLE,
|
|
title="Lab 1: Ideal Versus Sampled",
|
|
instructions="Keep the circuit shallow. Compare the ideal story to the sampled histogram and name which differences are only statistical.",
|
|
),
|
|
quiz_code(NOISE_LAB_QUIZ_A, "Noise Lab Checkpoint A"),
|
|
reflection_code(
|
|
"What does the ideal reference tell you that a single finite-shot histogram cannot?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 2: Same Circuit Under A Local Noise Profile
|
|
|
|
Keep the same circuit and protocol, but now inspect it under a demo noise model. This is where the three-view discipline becomes useful: the protocol did not change, but the evidence did.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
NOISE_IDEAL_EDITABLE,
|
|
title="Lab 2: Local Noise Preview",
|
|
instructions="Edit the circuit cautiously and compare the preview under a stable local noise profile.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
code_cell(
|
|
"""
|
|
ideal = QuantumCircuit(2, 2)
|
|
ideal.h(0)
|
|
ideal.cx(0, 1)
|
|
ideal.measure([0, 1], [0, 1])
|
|
|
|
ideal_counts = simulate_counts(ideal, shots=1024)
|
|
noisy_counts = simulate_counts(
|
|
ideal,
|
|
shots=1024,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
|
|
{
|
|
"ideal_counts": ideal_counts,
|
|
"noisy_counts": noisy_counts,
|
|
"ideal_probabilities": counts_to_probabilities(ideal_counts),
|
|
"noisy_probabilities": counts_to_probabilities(noisy_counts),
|
|
}
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Lab 3: Stress Test A Lean And A Stressed Candidate
|
|
|
|
The last lab compares two circuits under the same noise profile. The question is not which one is prettier. The question is which one carries a more robust structure for the same style of task.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
NOISE_STRESSED_EDITABLE,
|
|
title="Lab 3: Stressed Candidate Under Noise",
|
|
instructions="Edit the stressed candidate and compare it to a leaner alternative under the same noise assumptions.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
editable_lab_code(
|
|
NOISE_ROBUST_EDITABLE,
|
|
title="Lab 3B: Lean Candidate Under Noise",
|
|
instructions="Use the same protocol as the stressed candidate and compare which structure survives the local model better.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
quiz_code(NOISE_LAB_QUIZ_B, "Noise Lab Checkpoint B"),
|
|
markdown_cell(
|
|
"""
|
|
## Lab Debrief
|
|
|
|
These labs are deliberately repetitive in one sense: they keep forcing the same circuit family through different explanatory lenses. That repetition is useful. It trains the distinction between model, evidence, and distortion until the distinction becomes natural. The ideal reference says what clean behavior should mean. The sampled histogram says what repeated measurement produced in finite shots. The noisy histogram says what happened after a controlled distortion profile was added. If you can keep those three statements separate while the circuit changes, you are learning the right habit.
|
|
|
|
The debrief also reinforces another point: robustness comparison only works when the protocol stays fixed. If the measurement basis or reporting map changes at the same time as the structure, you no longer know which difference you are explaining. That is why this module is so insistent about controlled comparison.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Write a short note explaining which difference in the stressed-circuit result you think is structural rather than mere sampling."
|
|
),
|
|
reflection_code(
|
|
"What would you check next if you were not yet sure whether the observed drift came from noise or from a protocol mistake?"
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def build_noise_problems() -> dict:
|
|
cells = [
|
|
markdown_cell(
|
|
"""
|
|
# Simulation and Noise Models Problems
|
|
|
|
This notebook checks whether you can keep ideal, sampled, and noisy claims distinct under pressure. The target is not only factual knowledge. It is diagnostic discipline.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## How To Use This Notebook
|
|
|
|
Treat each question as a miniature debugging scenario. Ask yourself what kind of mismatch is being described and what evidence would strengthen or weaken the explanation.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
]
|
|
|
|
for heading, questions in NOISE_PROBLEM_SETS:
|
|
cells.append(
|
|
markdown_cell(
|
|
f"""
|
|
## {heading}
|
|
|
|
Use these quizzes to rehearse the distinctions that later verification and robustness work will depend on.
|
|
"""
|
|
)
|
|
)
|
|
cells.append(quiz_code(questions, heading))
|
|
|
|
cells.extend(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
## Mini Case: The Wrong Kind Of Noise Explanation
|
|
|
|
A weak explanation says, "the circuit got noisier because real quantum computers are noisy." That sentence is not false, but it is nearly useless. A stronger explanation says, "under this local error profile, the extra two-qubit layer created more opportunities for distortion, and the observed drift stayed aligned with that structural change even when the measurement protocol remained fixed." The second sentence is what this notebook is training.
|
|
|
|
Keep that contrast in mind while answering the quizzes above. The target is not to sound realistic. The target is to sound diagnostically precise.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Mini Case: Low Shots, Big Confidence
|
|
|
|
Another common mistake is to overread a small-shot run. A learner sees one surprising histogram and immediately writes a structural story around it. Sometimes that story is right. Often it is just too early. This case is worth naming because robust engineering judgment is as much about restraint as it is about insight. A good diagnosis knows when the evidence is still thin.
|
|
|
|
Keep that in mind while answering the reflections below. The right goal is not aggressive certainty. The right goal is justified certainty.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Written Checks
|
|
|
|
The reflections below are here to force complete diagnostic sentences rather than vague appeals to realism.
|
|
"""
|
|
),
|
|
reflection_code(
|
|
"Explain the difference between a circuit that is sampling-noisy and a circuit that is structurally vulnerable to the chosen local error model."
|
|
),
|
|
reflection_code(
|
|
"Write a short memo on how you would compare two candidate circuits under the same noise model without overclaiming from the results."
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Exit Condition
|
|
|
|
Move on only when you can classify mismatches instead of calling all of them noise without further thought.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
return notebook(cells)
|
|
|
|
|
|
def build_noise_studio() -> dict:
|
|
return notebook(
|
|
[
|
|
markdown_cell(
|
|
"""
|
|
# Simulation and Noise Models Studio
|
|
|
|
The studio asks you to act like a local-first experimentalist. You will compare candidate circuits under the same distortion model and defend a robustness judgment with explicit evidence.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Standard
|
|
|
|
Every design note in this notebook should state the ideal objective, the noise assumptions, and the structural reason one candidate appears more robust than another.
|
|
"""
|
|
),
|
|
code_cell(SETUP),
|
|
code_cell(COMMON_IMPORTS),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 1: Clean Reference Versus Noisy Reality
|
|
|
|
Start from the Bell-style reference and write the memo you would leave after seeing ideal and noisy evidence side by side.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
NOISE_IDEAL_EDITABLE,
|
|
title="Studio Brief 1: Clean Versus Noisy",
|
|
instructions="Keep the protocol fixed and write the explanation that separates ideal intent, sampled evidence, and noisy drift.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
reflection_code(
|
|
"What part of the observed drift do you think comes from the chosen noise model rather than from a changed question?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 2: Candidate Robustness Comparison
|
|
|
|
Compare the stressed and lean candidates under the same local model. Your judgment should be explicit about what stayed fixed and what changed.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
NOISE_STRESSED_EDITABLE,
|
|
title="Studio Brief 2: Stressed Candidate",
|
|
instructions="Inspect the heavier candidate under the local model and prepare the comparison memo.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
editable_lab_code(
|
|
NOISE_ROBUST_EDITABLE,
|
|
title="Studio Brief 2B: Lean Candidate",
|
|
instructions="Compare the leaner alternative under the same protocol and distortion model.",
|
|
context_source="""
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=build_demo_noise_model(two_qubit_error=0.08, readout_error=0.05),
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
reflection_code(
|
|
"Which candidate would you ship as the local teaching example, and what evidence actually supports that choice?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Brief 3: Change The Noise Assumptions Deliberately
|
|
|
|
Keep the circuit fixed and alter the local noise assumptions in the code. The goal is to see how much of your robustness claim depends on the chosen distortion profile.
|
|
"""
|
|
),
|
|
editable_lab_code(
|
|
NOISE_IDEAL_EDITABLE,
|
|
title="Studio Brief 3: Noise-Assumption Sensitivity",
|
|
instructions="Adjust the local error rates and explain which parts of your robustness judgment remain stable.",
|
|
context_source="""
|
|
custom_profile = build_demo_noise_model(single_qubit_error=0.002, two_qubit_error=0.12, readout_error=0.06)
|
|
noisy_preview = lambda circuit, shots=256: simulate_counts(
|
|
circuit,
|
|
shots=shots,
|
|
noise_model=custom_profile,
|
|
)
|
|
""",
|
|
context_name="noisy_preview",
|
|
),
|
|
quiz_code(NOISE_STUDIO_QUIZ, "Noise Studio Check"),
|
|
reflection_code(
|
|
"What is the main robustness smell you can now recognize in a circuit before simulating it under noise?"
|
|
),
|
|
reflection_code(
|
|
"Which part of your noise reasoning still feels too dependent on one chosen local model, and how would you test that weakness?"
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Rubric
|
|
|
|
Judge your studio memo with five questions. Did you state the ideal objective clearly? Did you state the noise assumptions clearly? Did you compare the candidates under the same protocol? Did you tie the observed gap back to a plausible structural reason? And did you admit where the conclusion still depends on the chosen local model? A memo that cannot answer those questions is still too soft for engineering use.
|
|
|
|
This rubric is intentionally demanding because later verification and capstone work will need exactly this style of disciplined uncertainty. The point is not to eliminate doubt. The point is to express it in a useful, testable form.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Review Standard
|
|
|
|
The best test of your memo is whether another engineer could disagree with it productively. That requires enough detail to challenge the protocol, the local assumptions, or the structural explanation. If your memo only announces that one candidate "felt more robust," it is still too private and intuitive. If it states the setup clearly enough that disagreement can become experimental, then it has crossed the line into useful engineering writing.
|
|
"""
|
|
),
|
|
markdown_cell(
|
|
"""
|
|
## Studio Exit
|
|
|
|
A strong outcome is a review memo that another engineer could trust: it states the local assumptions, compares the candidates fairly, and ties the observed gap back to circuit structure.
|
|
"""
|
|
),
|
|
]
|
|
)
|
|
|
|
|
|
def main() -> None:
|
|
outputs = {
|
|
MODULE_01_DIR / "lecture.ipynb": build_construction_lecture(),
|
|
MODULE_01_DIR / "lab.ipynb": build_construction_lab(),
|
|
MODULE_01_DIR / "problems.ipynb": build_construction_problems(),
|
|
MODULE_01_DIR / "studio.ipynb": build_construction_studio(),
|
|
MODULE_02_DIR / "lecture.ipynb": build_transpilation_lecture(),
|
|
MODULE_02_DIR / "lab.ipynb": build_transpilation_lab(),
|
|
MODULE_02_DIR / "problems.ipynb": build_transpilation_problems(),
|
|
MODULE_02_DIR / "studio.ipynb": build_transpilation_studio(),
|
|
MODULE_03_DIR / "lecture.ipynb": build_noise_lecture(),
|
|
MODULE_03_DIR / "lab.ipynb": build_noise_lab(),
|
|
MODULE_03_DIR / "problems.ipynb": build_noise_problems(),
|
|
MODULE_03_DIR / "studio.ipynb": build_noise_studio(),
|
|
}
|
|
|
|
for path, payload in outputs.items():
|
|
write_notebook(path, payload)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|