mirror of
https://github.com/saymrwulf/QuantumLearning.git
synced 2026-05-14 20:58:00 +00:00
2039 lines
149 KiB
Python
2039 lines
149 KiB
Python
#!/usr/bin/env python3
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
import hashlib
|
||
import json
|
||
from pathlib import Path
|
||
from textwrap import dedent
|
||
|
||
|
||
ROOT = Path(__file__).resolve().parents[1]
|
||
NOTEBOOKS_DIR = ROOT / "notebooks"
|
||
|
||
|
||
def markdown_cell(text: str) -> dict:
|
||
cleaned = "\n".join(
|
||
line.strip() if line.strip() else ""
|
||
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,
|
||
}
|
||
|
||
|
||
SETUP_CELL = """
|
||
from pathlib import Path
|
||
import sys
|
||
|
||
project_root = Path.cwd().resolve()
|
||
if project_root.name == "notebooks":
|
||
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))
|
||
"""
|
||
|
||
|
||
INTERACTIVE_IMPORTS = """
|
||
from quantum_learning.interactive import editable_circuit_lab, quiz_block, reflection_box, step_reference_table
|
||
"""
|
||
|
||
|
||
def meta_quiz_cell(questions: list[dict], heading: str) -> dict:
|
||
return code_cell(
|
||
f"""
|
||
quiz_block({questions!r}, heading={heading!r})
|
||
"""
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class TechnicalSpec:
|
||
filename: str
|
||
title: str
|
||
learning_objective: str
|
||
professional_relevance: str
|
||
key_vocabulary: list[tuple[str, str]]
|
||
mental_models: list[str]
|
||
canonical_story: str
|
||
design_lens: str
|
||
editable_imports: str
|
||
editable_context_names: list[str]
|
||
editable_code: str
|
||
step_refs: list[dict[str, str]]
|
||
analysis_code: str
|
||
analysis_caption: str
|
||
common_mistakes: list[str]
|
||
heuristics: list[str]
|
||
quiz_a: list[dict]
|
||
quiz_b: list[dict]
|
||
mastery_gate: str
|
||
stretch_prompts: list[str]
|
||
|
||
|
||
def bullet_block(items: list[str], marker: str = "-") -> str:
|
||
return "\n".join(f"{marker} {item}" for item in items)
|
||
|
||
|
||
def vocabulary_block(items: list[tuple[str, str]]) -> str:
|
||
return "\n".join(f"- **{term}**: {definition}" for term, definition in items)
|
||
|
||
|
||
def long_lecture_part_one(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes I
|
||
|
||
The learning objective of this notebook is not simply to let you execute a working example. The real objective is: **{spec.learning_objective}**. That wording matters because professional growth is always about a capability, not a demo. A demo proves that one arrangement of code can run. A capability means you understand why the arrangement works, what would break it, how to modify it, and how to explain it to another engineer. When you study this notebook, do not ask only whether the final plot or diagram looks right. Ask whether you could reconstruct the same idea from memory, defend the design choices aloud, and spot a broken version of the construction if someone quietly changed one line.
|
||
|
||
The professional relevance is direct. {spec.professional_relevance} That means this notebook should be read with a design mindset. A design mindset is slower than a copying mindset. You pause at each transformation, ask what role it plays, ask which other transformation could play a similar role, and ask how the diagram communicates the intention. Those habits feel inefficient at first, but they are exactly what later allows you to move from classroom-scale circuits to research or engineering-scale circuits.
|
||
|
||
The most important reading habit in this notebook is to keep two descriptions in your head at the same time. One description is the code. The other description is the process that the code is trying to induce. The code is just the interface. The process is the actual concept. If you over-identify with the code listing, every new notebook will feel like new syntax. If you identify with the process instead, later notebooks become variations on a familiar set of design moves.
|
||
|
||
Here is the mental stance to hold while reading: the circuit is a time-ordered argument. Each gate says, “because of what I know about the current state, the next state should look like this.” If you cannot explain that argument step by step, you have not yet turned notebook exposure into engineering understanding. That is why the notebook includes editable labs and reference tables. The point is to force a conversation between the code, the diagram, and your explanation.
|
||
|
||
The key vocabulary for this notebook is:
|
||
|
||
{vocabulary_block(spec.key_vocabulary)}
|
||
|
||
These terms are not decoration. Each one names a handle you need in order to reason clearly. Beginners often say vague things like “the circuit sort of does a quantum thing here.” That is not good enough. Precise language reduces confusion. It lets you distinguish preparation from transformation, correlation from measurement, and implementation detail from conceptual goal. If you use precise words, you will think more precisely.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_two(spec: TechnicalSpec) -> str:
|
||
mental_model_lines = "\n".join(f"- {item}" for item in spec.mental_models)
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes II
|
||
|
||
A good way to approach this notebook is to imagine that you are the reviewer of a junior engineer’s circuit. You are not allowed to be impressed by the diagram merely because it contains quantum gates. You must be able to ask brutal but fair questions. Why is each step there? Which line is carrying the logical burden? What would happen if we removed or moved a gate? Which pieces are conceptually essential, and which pieces are bookkeeping? Those questions are how real design judgment develops.
|
||
|
||
For this notebook, the most useful mental models are:
|
||
|
||
{mental_model_lines}
|
||
|
||
Notice that these are models, not formulas. Formulas matter, but the formulas land much better when they attach to stable intuitions. A professional designer can move back and forth between intuition and formalism. The intuition says what kind of structural event is happening. The formalism says exactly how to calculate it. If you only have intuition, you become hand-wavy. If you only have formalism, you become brittle and slow. The notebook text is intentionally trying to build both at once.
|
||
|
||
The canonical story in this notebook is: {spec.canonical_story} This story is worth reading several times because it encodes the design narrative of the example. The point is not merely that the example works. The point is that it gives you a vocabulary for talking about cause and effect inside the circuit. You should be able to retell that story while pointing alternately to the code and the diagram. That is why the next cells give you a reference table with numbered markers and an editable circuit lab whose comments use the same markers.
|
||
|
||
Keep the following design lens in mind while you work: {spec.design_lens} The design lens is what prevents the notebook from becoming just another gallery of tricks. Later, when you face a more complicated circuit, this lens will help you recognize that the new design is still built from a small number of recurring ideas. That recognition is one of the main signs that you are leaving the amateur stage.
|
||
|
||
Finally, accept that slowness here is productive. If a beginner notebook feels almost embarrassingly basic, that is usually the exact place where hidden weakness lives. Engineers who rush past foundations are often the same people who later cannot explain why transpilation, noise, or verification changed the story. Slow down, narrate every move, and make your understanding explicit.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_three(spec: TechnicalSpec) -> str:
|
||
mistakes = bullet_block(spec.common_mistakes)
|
||
heuristics = bullet_block(spec.heuristics)
|
||
stretches = bullet_block(spec.stretch_prompts)
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes III
|
||
|
||
Once you have run the editable lab, the next skill is interpretation. Interpretation means that you do not merely observe an output; you explain why that output is structurally consistent with the design. Many beginners stop one step too early. They see that a circuit “works” and take that as proof of understanding. It is not. Understanding starts when you can say why the output pattern is plausible, which aspect of the design produced it, and which alternative designs might produce a similar signature for different reasons.
|
||
|
||
This is also where common mistakes matter most. The usual traps in this notebook are:
|
||
|
||
{mistakes}
|
||
|
||
Read those carefully and check whether one of them is currently happening in your own head. That is a serious exercise, not a rhetorical flourish. Good teaching is partly the art of naming the wrong mental picture before it hardens into habit. Once a mistaken picture hardens, every later notebook becomes harder to learn because you are trying to place new knowledge on top of a warped frame.
|
||
|
||
To counter those mistakes, keep the following design heuristics close at hand:
|
||
|
||
{heuristics}
|
||
|
||
A heuristic is not a theorem. It is a professional shortcut for seeing what deserves attention first. Heuristics become especially valuable once notebooks stop being tiny. In large circuits, you cannot afford to rediscover every conceptual anchor from scratch. You need habits of attention. This notebook is deliberately training those habits early.
|
||
|
||
If you want to push beyond passive understanding, use the stretch prompts as mini design studios:
|
||
|
||
{stretches}
|
||
|
||
Treat each stretch prompt as a design review task. Make a prediction, change the circuit, compare the result to your prediction, and then explain the difference in plain language. If you do that consistently, the notebook stops being static literature and becomes a training environment. That is the actual goal.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_four(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes IV
|
||
|
||
There is a difference between seeing a circuit and *owning* a circuit. Seeing means you can follow the syntax while the notebook is in front of you. Owning means you can reconstruct the design intention later, explain what would happen if one line changed, and say which parts of the design are conceptually central. The goal of this notebook is ownership. That is why the prose is intentionally repetitive in a useful way. It keeps returning to the learning objective from several angles because strong understanding usually needs multiple passes through the same idea, not one quick exposure.
|
||
|
||
Another important professional habit is self-explanation. After you run any important cell, pause and say what the result means in relation to the notebook objective: {spec.learning_objective}. If you cannot connect the result back to that objective, you are probably collecting observations without integrating them. Integration is the real challenge. The course is trying to make integration visible and trainable. Quizzes help because they reveal whether your language is crisp. Editable labs help because they reveal whether your understanding survives change. Reflection prompts help because they force you to produce explanations rather than merely recognize them.
|
||
|
||
Also notice how the notebook keeps connecting local ideas to later professional roles. This is not decorative motivation. It is a memory device. When you understand how a small foundational lesson will later reappear in synthesis, transpilation, verification, or capstone review, you encode it more deeply. The present notebook becomes a seed for later concepts instead of a standalone episode.
|
||
|
||
Finally, remember that being lost is often a signal that the transition between representations is weak. The course therefore tries to keep translating the same idea into prose, code, diagram, quiz question, and editable lab. Those translations are not redundant. They are the mechanism by which a fragile understanding becomes robust.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_five(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes V
|
||
|
||
When people say they want to become “advanced,” they often imagine harder circuits. Harder circuits do matter, but what really changes with expertise is the quality of attention. An amateur looks for what to type next. A designer looks for structural burden, hidden assumption, reusable pattern, and likely failure mode. This notebook is therefore already training advanced attention inside a beginner or intermediate topic. That is why the explanations may feel more detailed than a typical tutorial. The detail is not filler; it is attention training.
|
||
|
||
Read this notebook as an invitation to slow your eye down. Which code comment is naming an intention? Which line is merely a local implementation detail? Which part of the diagram would you point to if asked to explain the central mechanism? Which result would convince you that your understanding is wrong? Those are design-review questions, and they can be practiced long before you become an expert. In fact, they *must* be practiced long before, because later advanced work depends on them.
|
||
|
||
The notebook objective remains the same throughout: {spec.learning_objective}. If you leave with only a hazy sense that the examples were interesting, the notebook has failed. If you leave able to explain the mechanism, edit the circuit, answer the quizzes, and diagnose your own confusion in precise terms, then the notebook has done its job. That is the standard you should hold yourself to. The course is meant to feel demanding in exactly that way.
|
||
|
||
Use the remaining cells to verify that demand. Write reflections honestly. If a quiz exposes a weak point, that is useful information. If an edit breaks the circuit in a way you did not expect, that is useful information. The platform is becoming more interactive precisely so those moments happen early and often, where they can still be turned into learning rather than embarrassment.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_six(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes VI
|
||
|
||
Another reason the notebook is this text-heavy is that genuine understanding is almost always narrower than learners think. You may feel that you “basically get it” after the first successful run. But what does that phrase actually mean? Can you explain the mechanism without reading? Can you alter the circuit and retain control of the narrative? Can you answer a multiple choice question designed to expose a subtle confusion? Can you review someone else’s variation and say what is structurally different? If the answer to those questions is still no, then the feeling of understanding is real but premature. These notebooks are trying to move you from premature understanding to stable understanding.
|
||
|
||
Stable understanding has a practical signature: it survives translation. If the idea still makes sense when you move from markdown to code, from code to diagram, from diagram to histogram, from histogram back to explanation, then the idea is becoming robust. That is why this notebook keeps revisiting the same material through several interfaces. The repetition is not because the course assumes you are slow. The repetition is because circuit design itself is multi-representational. Real work requires you to hold several representations of the same structure at once, and good pedagogy should reflect that fact.
|
||
|
||
This is also why your notes matter. If you are studying seriously, the reflection prompts should not remain empty. Write what you thought before the experiment, what the circuit actually did, and what sentence now explains the difference best. Written explanation is a debugging tool for thought. It reveals vagueness that silent recognition can hide. If a sentence feels hard to write, that difficulty is diagnostic. Follow it.
|
||
|
||
Keep returning to the notebook objective: {spec.learning_objective}. The objective is the filter through which every output should be interpreted. The platform is not trying to flood you with local facts. It is trying to build durable design habits. Those habits come from focused repetition around a sharply defined objective, not from wandering exposure.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_seven(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Lecture Notes VII
|
||
|
||
One of the most important professional transitions is the move from “what happened?” to “what should have happened, and why?” That difference sounds small, but it is huge. The first question is observational. The second question is explanatory and normative. Good designers live in the second question. They are not satisfied by a plot, a diagram, or a successful run. They want a reason the result deserves trust. This notebook is training that habit even if the example itself is still relatively small.
|
||
|
||
The deeper reason this matters is that later circuit design work becomes ambiguous. You will face multiple candidate designs, backend constraints, noisy distortion, and verification tasks that do not yield a single obvious answer. In that world, habits formed in the beginner notebooks matter enormously. If your habit is to run first and rationalize later, ambiguity will hurt you. If your habit is to predict, inspect, compare, and justify, ambiguity becomes workable. That is why even this notebook expects explanation discipline: it is building the habits you will need when the stakes become higher.
|
||
|
||
There is also an emotional reason for this level of explanation. Many learners feel lost not because the ideas are impossible, but because the course materials they encounter fail to tell them what deserves attention. A dense, guided notebook can reduce that kind of loss. It says: here is the point of the section, here is the common confusion, here is the reason the example was chosen, here is the meaning of the output, here is the question you should now be able to answer. That guidance is not indulgent. It is efficient.
|
||
|
||
Use the remainder of the notebook accordingly. Read slowly, answer honestly, edit boldly, and write explanations in full sentences. If the notebook feels demanding, that is correct. Professional design capability is demanding. The platform should respect that instead of pretending otherwise.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_eight(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Extended Lecture Notes VIII
|
||
|
||
The phrase learning objective can sound administrative, but in a serious course it should function like a contract. The notebook says it will train a particular ability, and the notebook should then repeatedly expose that ability from different angles until it becomes visible, testable, and improvable. In this notebook the contract is: {spec.learning_objective}. Read that sentence slowly. It contains several smaller obligations hidden inside one larger sentence. Usually those obligations include vocabulary, structural reading, state or process prediction, behavioral interpretation, and the ability to tolerate small modifications without losing the conceptual plot. If one of those obligations is missing, your understanding may feel better than it really is.
|
||
|
||
This is why the notebook keeps returning to the same central mechanism instead of constantly introducing new examples. Variety is seductive, but repetition around a sharp objective is often more pedagogically powerful. If the same design idea can survive explanation in prose, code, diagram, quiz form, and editable lab form, then your grasp of it is becoming durable. If the idea only makes sense in one representation, you are still dependent on that representation and are not yet designing freely.
|
||
|
||
Another way to say the same thing is this: the notebook is trying to teach you what to pay attention to. That may be the single most important beginner-to-professional transition in any technical field. Experts and amateurs often look at the same object but do not see the same burden. The amateur sees a list of steps. The expert sees the lines carrying the main causal load, the lines doing local bookkeeping, the assumptions the design depends on, and the likely places where later constraints will distort the picture. A notebook like this cannot grant expertise immediately, but it can start shaping the same habits of attention.
|
||
|
||
When you work through the editable cells, keep asking four questions. First, which part of the circuit is the main mechanism rather than mere setup? Second, how does the diagram communicate that mechanism more clearly than raw code? Third, what result would count as evidence that your story about the mechanism is wrong? Fourth, how would the same design need to change under later hardware or noise constraints? Those questions are worth practicing now because they are the same family of questions you will use in professional design review later.
|
||
|
||
If the notebook starts to feel repetitive, test whether the repetition is doing work. Can you now explain the objective more precisely than you could ten minutes ago? Can you point to a specific marker and state why it matters for the whole construction? Can you describe what would likely happen if that marker were altered? If the answer is yes, the repetition is succeeding. It is turning exposure into control.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_nine(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Lecture Notes IX
|
||
|
||
A major source of beginner confusion is the unstable relationship between representations. You may understand the prose but not the code. Or the code but not the diagram. Or the diagram but not the output statistics. Professional circuit design requires those representations to reinforce rather than compete with one another. That is why this notebook is intentionally over-instrumented. It does not trust any single view by itself. The prose names the intention, the code implements it, the diagram spatializes it, and the output gives you something behavioral to inspect. Strong understanding emerges from agreement across those views.
|
||
|
||
The code-to-diagram reference markers are therefore not cosmetic labels. They are anchors for translation. Translation is one of the hidden core skills in engineering. When someone says “this circuit prepares the right correlation pattern but pays too much routing overhead,” they are translating among design intention, circuit structure, and hardware consequence in one sentence. If you train that translation now, later advanced notebooks will feel like larger versions of a familiar game rather than entirely new worlds.
|
||
|
||
It also matters that the editable lab shows both a graphic and a text rendering. The graphic is better for seeing large-scale structure, symmetry, and ordering across wires. The text rendering is better for checking exact sequencing and spotting how the code expanded into a circuit object. Different renderings reveal different truths. A mature workflow uses both. If you only trust the pretty picture, you can miss a subtle sequencing detail. If you only trust the textual representation, you can miss a larger structural pattern. A serious notebook should train you to move between them without friction.
|
||
|
||
Try to notice, too, how explanation quality changes when you point to a representation while speaking. “This line applies a Hadamard” is technically true but often pedagogically weak. “This marker creates the branch point that spreads amplitude across alternatives, which is why the later controlled step can create correlation instead of acting on a single classical-looking path” is much better. The second explanation links code, picture, and mechanism. It is longer because it is better. Engineering language becomes powerful when it compresses the right distinctions, not when it is short for its own sake.
|
||
|
||
That is the deeper reason the notebooks are now much more literary. Text is not being added to compensate for weak code. Text is being used to train precise noticing. The more precisely you can narrate a circuit, the more precisely you can later design, debug, or review one.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_ten(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Lecture Notes X
|
||
|
||
Professional growth also requires a change in how you treat mistakes. In amateur mode, a mistake feels like proof that you do not understand the topic. In design mode, a mistake is evidence about which internal model is currently running your decisions. That is a much more useful interpretation. If you alter the circuit and the output surprises you, that surprise is not noise in the learning process. It is the learning process. It reveals the exact place where your prediction engine diverged from the actual mechanism.
|
||
|
||
This notebook is therefore designed to create small, recoverable failures. The quizzes create retrieval failures. The editable labs create prediction failures. The reflection prompts create language failures where you discover you cannot yet say the idea as cleanly as you thought. Those failures are not a side effect. They are the point. A notebook that only ever makes you feel comfortable may be pleasant, but it will usually not be enough to create mastery.
|
||
|
||
There is another reason to normalize this now. Later circuit design work becomes multi-objective. You will weigh depth, width, connectivity, synthesis cost, error sensitivity, measurement strategy, and verification burden at the same time. In that setting, mistakes become even more inevitable. What distinguishes a professional is not magical avoidance of error. It is fast, structured recovery. Structured recovery starts with habits like: restate the objective, inspect the main mechanism, compare expected and observed behavior, isolate the smallest meaningful change, and articulate the new hypothesis before touching the circuit again. Even beginner notebooks can practice that discipline.
|
||
|
||
Use the variation lab in that spirit. Do not change things randomly and hope for insight. Pick a deliberate change tied to the stretch prompt, state your prediction in full sentences, then render and inspect. If the result disagrees with your prediction, write down exactly which sentence of your earlier story needs revision. That act of revision is a design skill. It is the same skill later used when transpilation distorts an elegant ideal circuit, or when a noise model exposes that a clean whiteboard intuition was incomplete.
|
||
|
||
In short, the notebook is not merely showing you a canonical circuit. It is teaching you how to respond when a circuit stops being canonical and starts becoming yours.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_eleven(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Lecture Notes XI
|
||
|
||
A final point worth emphasizing is transfer. A notebook succeeds professionally only if the understanding it builds can be reused elsewhere. Transfer is harder than recognition. Recognition says, “I remember seeing this.” Transfer says, “I can detect the same structural idea inside a new problem even when the surface presentation has changed.” The notebook therefore keeps pointing beyond itself. It wants you to see how today’s local lesson becomes tomorrow’s building block in synthesis, transpilation, verification, or capstone review.
|
||
|
||
This matters because real circuit design rarely announces its conceptual ingredients politely. A larger design may conceal a familiar principle inside many layers of implementation detail. If your earlier learning was shallow, the detail overwhelms you. If your earlier learning was principled, you start spotting the old mechanism inside the new setting. That spotting is one of the deepest rewards of good pedagogy. Suddenly complexity becomes decomposable.
|
||
|
||
Transfer also depends on your ability to abstract without becoming vague. You do not want to say only “this is kind of like entanglement again” or “this looks like another measurement issue.” You want to name the operative pattern with enough precision that it can guide a design decision. That precision is exactly why the notebook keeps circling around vocabulary, design lens, and mastery gate language. The words are not there to sound formal. They are there so that, later, you can think with enough granularity to act well.
|
||
|
||
As you finish the notebook, ask yourself a transfer question directly: if you met a new circuit tomorrow that contained the same core lesson but different gate order, different naming, or a different visualization style, what clues would tell you that the same design idea is present? If you can answer that, your learning is starting to generalize. If you cannot answer it yet, the notebook has shown you what still needs reinforcement.
|
||
|
||
That is a demanding standard, but it is the correct one for a course aimed at professional circuit design. The goal is not merely that you remember this notebook. The goal is that you carry its reasoning power into circuits the notebook never showed you.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def long_lecture_part_twelve(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Lecture Notes XII
|
||
|
||
Before you leave the notebook, convert the lesson into a personal review rubric. A good rubric is specific enough to be checkable and small enough to be reused. For this notebook, a starting rubric could be: can I state the learning objective clearly, can I identify the circuit’s main mechanism, can I connect each numbered marker to a visible diagram change, can I answer the quizzes without guessing, can I alter the design deliberately, and can I explain what still feels unstable? If the answer is no to any one of those, that does not mean failure. It means you have located the next repair.
|
||
|
||
Notice how different this is from a completion mindset. Completion asks whether the notebook has been opened, run, or skimmed. A rubric asks whether the capability is present. That distinction becomes more important as the course gets harder. Advanced notebooks are full of opportunities to confuse exposure with mastery. A rubric interrupts that confusion.
|
||
|
||
The long-term professional benefit is substantial. Designers who use rubrics learn faster because they do not rely on mood to judge progress. They rely on observable signs. Could I explain the design to another engineer? Could I predict the effect of a modification? Could I defend why one version is preferable under a stated objective? Those are observable questions. They make your study more honest and therefore more efficient.
|
||
|
||
Keep that standard in mind as you move forward. The notebook is intentionally giving you more text, more interaction, and more chances to test yourself because the final ambition of the platform is serious. If you engage the material fully, these notebooks will stop feeling like isolated lessons and start functioning like deliberate rehearsals for real circuit design work.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def step_reference_intro() -> str:
|
||
return dedent(
|
||
"""
|
||
## Code-To-Diagram Reference
|
||
|
||
The next table is the bridge between code and graphics. Each marker appears in the editable code comments. Your job is to look at a marker, identify the relevant line of code, find the corresponding structural change in the circuit diagram, and explain why that change matters for the learning objective of the notebook.
|
||
|
||
This is the closest notebook-level substitute for clickable diagram editing that we can do robustly in a local-first workflow. It is still very useful. The markers force your attention to move back and forth between three representations of the same object: source code, circuit graphic, and conceptual explanation.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def reflection_cell(prompt: str) -> dict:
|
||
return code_cell(
|
||
f"""
|
||
reflection_box({prompt!r})
|
||
"""
|
||
)
|
||
|
||
|
||
def analysis_markdown(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Guided Analysis
|
||
|
||
{spec.analysis_caption}
|
||
|
||
Do not treat the next code cell as an optional extra. It is the interpretive half of the notebook. The editable circuit lab above helps you manipulate structure. The analysis cell below helps you connect that structure to behavior, summaries, counts, or transformations that can actually be inspected. The two parts belong together. If you only edit the circuit without interpreting the result, you are playing. If you interpret the result without editing the circuit, you are reading. The notebook is trying to make you do both.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def mastery_markdown(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Mastery Gate
|
||
|
||
{spec.mastery_gate}
|
||
|
||
If you cannot yet do that, do not panic. But do not pretend you have finished the notebook either. A mastery gate is not punishment. It is calibration. The whole course works only if you tell the truth about which abilities are now stable and which are still fragile.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def editable_lab_cell(spec: TechnicalSpec) -> dict:
|
||
context_pairs = ", ".join(f'"{name}": {name}' for name in spec.editable_context_names)
|
||
return code_cell(
|
||
f"""
|
||
editable_code = {spec.editable_code!r}
|
||
|
||
editable_circuit_lab(
|
||
initial_code=editable_code,
|
||
context={{{context_pairs}}},
|
||
title={spec.title!r},
|
||
instructions="Edit the commented code markers, re-render the circuit, and compare the changed code to the changed diagram.",
|
||
)
|
||
"""
|
||
)
|
||
|
||
|
||
def step_reference_cell(spec: TechnicalSpec) -> dict:
|
||
return code_cell(
|
||
f"""
|
||
step_reference_table({spec.step_refs!r})
|
||
"""
|
||
)
|
||
|
||
|
||
def quiz_cell(questions: list[dict], heading: str) -> dict:
|
||
return code_cell(
|
||
f"""
|
||
quiz_block({questions!r}, heading={heading!r})
|
||
"""
|
||
)
|
||
|
||
|
||
def vocabulary_quiz_questions(spec: TechnicalSpec) -> list[dict]:
|
||
terms = [term for term, _ in spec.key_vocabulary]
|
||
questions = []
|
||
for index, (term, definition) in enumerate(spec.key_vocabulary[:3]):
|
||
options = terms[index:] + terms[:index]
|
||
questions.append(
|
||
{
|
||
"prompt": f"Which term best matches this definition: {definition}?",
|
||
"options": options,
|
||
"correct_index": options.index(term),
|
||
"explanation": f"`{term}` is the best match because it names {definition}.",
|
||
}
|
||
)
|
||
return questions
|
||
|
||
|
||
def marker_quiz_questions(spec: TechnicalSpec) -> list[dict]:
|
||
markers = [step["marker"] for step in spec.step_refs]
|
||
questions = []
|
||
for step in spec.step_refs[:3]:
|
||
questions.append(
|
||
{
|
||
"prompt": (
|
||
"Which numbered marker should you inspect first if you want to study this "
|
||
f"diagram effect: {step['diagram_effect']}?"
|
||
),
|
||
"options": markers,
|
||
"correct_index": markers.index(step["marker"]),
|
||
"explanation": (
|
||
f"{step['marker']} is correct because it is the step where "
|
||
f"{step['why_it_matters']}"
|
||
),
|
||
}
|
||
)
|
||
return questions
|
||
|
||
|
||
def variation_lab_markdown(spec: TechnicalSpec) -> str:
|
||
return dedent(
|
||
f"""
|
||
## Variation Lab
|
||
|
||
The first editable lab asked you to understand the canonical circuit faithfully. This second lab asks you to practice deliberate alteration. The challenge prompt is: **{spec.stretch_prompts[0]}**.
|
||
|
||
Use the same marker comments and the same code-to-diagram discipline, but now work like a junior designer in a supervised design review. Make one intentional change at a time. Before each render, write down what you expect to happen to the circuit graphic, the text diagram, and any measured behavior. After the render, compare your prediction to the result and decide whether the notebook’s learning objective is still being served or has been broken.
|
||
|
||
This second lab matters because professional circuit design is not built by memorizing one correct object. It is built by making controlled variations and retaining explanatory control while the object changes.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def variation_lab_cell(spec: TechnicalSpec) -> dict:
|
||
context_pairs = ", ".join(f'"{name}": {name}' for name in spec.editable_context_names)
|
||
instructions = (
|
||
f"Implement this deliberate variation challenge: {spec.stretch_prompts[0]} "
|
||
"Keep the marker comments visible, re-render after each change, and explain how the "
|
||
"code change appears in the diagram and in any observed behavior."
|
||
)
|
||
return code_cell(
|
||
f"""
|
||
variation_code = {spec.editable_code!r}
|
||
|
||
editable_circuit_lab(
|
||
initial_code=variation_code,
|
||
context={{{context_pairs}}},
|
||
title={f"{spec.title} Variation Lab"!r},
|
||
instructions={instructions!r},
|
||
)
|
||
"""
|
||
)
|
||
|
||
|
||
def start_here_extended_reading_one() -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Orientation I
|
||
|
||
A serious self-study notebook has to do more than present information. It has to manage attention. That is especially true in a subject like circuit design, where a beginner may not yet know which distinctions are foundational and which details are secondary. One function of this notebook is therefore to teach you how to allocate attention. You should not read the later technical notebooks the way you might skim a blog post or a quick-start guide. You should read them the way an apprentice studies a worked example in a demanding craft: slowly, with repeated passes, and with a willingness to stop at a single sentence until it becomes clear.
|
||
|
||
This is also why the notebooks now contain much more lecture-style prose. Dense writing is doing practical work. It is telling you what the example is for, which misconception to avoid, which output deserves interpretation, and what ability you should be able to demonstrate at the end. Without that guidance, beginners often oscillate between two bad extremes. Either they feel intimidated by every line, or they rush through and assume that successful execution equals understanding. The course is trying to avoid both extremes.
|
||
|
||
The most useful study rhythm is not complicated, but it does require discipline. Read a section of prose. Pause and summarize it in your own words. Predict what the next circuit cell or analysis cell is going to show. Run the cell. Compare the result to your prediction. If the result surprises you, treat that surprise as data about your mental model. Then answer the quiz or reflection prompt honestly. This rhythm is slower than passive clicking, but it produces much stronger learning because every step forces interpretation.
|
||
|
||
Another important point is that the notebooks are not trying to entertain you into mastery. They are trying to train you into mastery. Training is necessarily repetitive. It comes back to central ideas multiple times because the point is not novelty. The point is control. By the time you leave a notebook, the main idea should feel easier to explain, easier to manipulate, and easier to recognize in a new setting. If that is happening, then the notebook is doing its job.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def start_here_extended_reading_two() -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Orientation II
|
||
|
||
It is also worth being explicit about what “interactive” should mean in this course. Interactivity is not a gimmick and it is not just a nicer user interface. Real interactivity means that the notebook reacts to your reasoning. A multiple choice quiz reacts by exposing whether your current explanation survives retrieval. An editable circuit lab reacts by revealing whether your explanation survives modification. A reflection box reacts by revealing whether your explanation survives translation into your own language. Those are different tests, and together they form a much stronger learning environment than code cells alone.
|
||
|
||
This matters because circuit design is not one skill. It is a bundle of linked abilities: you have to read diagrams, map them to code, anticipate behavior, reason about structure, and communicate your understanding clearly enough that another engineer could review it. The course therefore uses several different interaction modes to exercise those different abilities. If you use only the code cells and ignore the quizzes or writing prompts, you are training only part of the required skill stack.
|
||
|
||
You should also know how to respond when the notebook feels too dense. Do not immediately interpret density as failure. Instead, diagnose the exact friction point. Are you missing vocabulary? Are you unable to track time ordering in the diagram? Are you confused by what is being measured versus what is being transformed? Are you able to repeat the explanation while reading it but not from memory? Each of those failures points to a different repair strategy. A strong learner does not merely persist emotionally. A strong learner improves the resolution of the diagnosis.
|
||
|
||
Finally, treat the early notebooks with respect. Foundational topics are not “baby material” if they are the place where your language, visual literacy, and explanatory habits are being formed. Professional designers are often distinguished less by secret advanced tricks than by extraordinary cleanliness in fundamentals. That is one of the governing assumptions of the whole platform.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def professional_path_extended_reading_one() -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Path Notes I
|
||
|
||
Backward-designed curriculum can sound abstract until you feel its practical force. The practical force is this: once the final professional role is named precisely, many tempting teaching decisions become obviously insufficient. If the final role is independent circuit designer under constraints, then a course built around isolated novelty examples is not enough. If the final role includes review, debugging, adaptation, and defense, then a course that measures only whether a notebook ran successfully is not enough. The capstone standard reaches backward and reshapes every earlier lesson.
|
||
|
||
That reshaping is the real reason for the stage ladder. A stage is not just a folder label. It is a claim about what kind of judgment you should now possess. At the earliest stage, the judgment is local: can you read the circuit at all, and can you distinguish the important objects? At a slightly later stage, the judgment becomes dynamical: can you predict how state, basis, or measurement will respond to a design choice? Later still it becomes architectural: can you think in reusable blocks and structured experiments rather than flat strings of gates? Eventually it becomes strategic: can you compare alternatives under hardware, synthesis, and noise constraints? Seeing the path in those terms makes the course much more coherent.
|
||
|
||
The stage ladder also protects against a common self-study trap: mistaking interest for preparation. You may be highly motivated by error correction, encoded circuits, or architecture papers, but if the earlier layers are weak then the advanced material becomes something you admire rather than something you can manipulate. Backward design does not deny ambition. It channels ambition into prerequisites. It says: if this advanced capability truly matters, which earlier capabilities must become automatic first? That question is often more valuable than asking which advanced topic looks exciting next.
|
||
|
||
A well-designed apprenticeship path therefore does two things simultaneously. It raises the standard by naming a demanding final role, and it lowers confusion by decomposing that role into visible subskills. That combination is what the notebook is trying to provide.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def professional_path_extended_reading_two() -> str:
|
||
return dedent(
|
||
"""
|
||
## Extended Path Notes II
|
||
|
||
There is also a deeper pedagogical principle behind the course structure: each stage changes the unit of thought. Beginners often think in lines of code. More developed learners begin to think in gates and local transformations. Later they think in subcircuits, patterns, invariants, and constraints. The jump to professional design happens when you can choose the right unit of thought for the problem in front of you and move up or down that ladder without getting lost. A course that only presents topics without highlighting these changing units of thought will often feel fragmented, even if its content is correct.
|
||
|
||
The stage ladder is therefore not merely chronological. It is representational. It tells you what kind of object should now be salient to you. In an early notebook, a control-target relationship may be the key object. In a later notebook, the key object may be a synthesis pattern or a routing bottleneck. In a capstone, the key object may be a design tradeoff backed by evidence. By naming these shifts directly, the course helps you notice whether your internal way of seeing circuits is actually evolving.
|
||
|
||
This is one reason the platform now emphasizes writing inside the notebooks. Written explanation can make invisible representational changes visible. If you compare how you describe a circuit at the start of the course to how you describe one several stages later, you should hear a difference. Early explanations will be local and syntactic. Later explanations should mention burden, purpose, constraint, degradation mode, or verification strategy. That change in language reflects a real change in cognition.
|
||
|
||
The final reason to care about this path notebook is motivational in the precise sense, not the sentimental sense. Motivation becomes much stronger when effort is legible. If you know why a beginner notebook exists, you can invest in it honestly. If you do not know why it exists, you are tempted either to dismiss it or to drown in it. A visible staircase solves both problems by showing what each rung is preparing you to do next.
|
||
"""
|
||
).strip()
|
||
|
||
|
||
def build_technical_notebook(spec: TechnicalSpec) -> dict:
|
||
cells = [
|
||
markdown_cell(
|
||
f"""
|
||
# {spec.title}
|
||
|
||
This notebook is built as lecture notes plus a local design studio. Read the markdown carefully, then use the interactive cells to test whether your mental model is actually working.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
f"""
|
||
## Learning Objective
|
||
|
||
**Primary objective:** {spec.learning_objective}
|
||
|
||
By the end of the notebook, you should be able to explain the central idea in plain language, modify the canonical circuit without losing track of its meaning, answer concept-check questions without guessing, and connect the notebook’s local lesson to later professional design work.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
f"""
|
||
## Why This Matters Professionally
|
||
|
||
{spec.professional_relevance}
|
||
|
||
The right way to read this notebook is therefore not “how do I finish it?” but “what professional skill is this page trying to install into me?” Every section, quiz, and editable lab is pointed at that question.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Study Method For This Notebook
|
||
|
||
Use the following rhythm all the way through:
|
||
|
||
1. read the explanation before you execute the next code cell
|
||
2. state a prediction out loud or in writing
|
||
3. run the cell
|
||
4. compare the output to your prediction
|
||
5. explain any mismatch without hand-waving
|
||
|
||
If you skip the prediction step, you lose most of the learning value. Passive recognition is much weaker than active prediction.
|
||
"""
|
||
),
|
||
markdown_cell(long_lecture_part_one(spec)),
|
||
markdown_cell(long_lecture_part_two(spec)),
|
||
markdown_cell(long_lecture_part_four(spec)),
|
||
markdown_cell(long_lecture_part_eight(spec)),
|
||
code_cell(SETUP_CELL),
|
||
code_cell(
|
||
spec.editable_imports.strip()
|
||
+ "\n\n"
|
||
+ INTERACTIVE_IMPORTS.strip()
|
||
),
|
||
markdown_cell(step_reference_intro()),
|
||
step_reference_cell(spec),
|
||
quiz_cell(vocabulary_quiz_questions(spec), "Vocabulary Drill"),
|
||
markdown_cell(
|
||
"""
|
||
## Editable Circuit Lab
|
||
|
||
The next cell is the main interactive object in the notebook. The code comments contain numbered markers that match the reference table above. Edit the code, render the result, and compare what changed in the code to what changed in the circuit diagram. If the circuit contains measurements, the widget will also preview local counts so you can connect the structure to the observed outcome.
|
||
"""
|
||
),
|
||
editable_lab_cell(spec),
|
||
markdown_cell(analysis_markdown(spec)),
|
||
code_cell(spec.analysis_code),
|
||
quiz_cell(marker_quiz_questions(spec), "Marker Drill"),
|
||
quiz_cell(spec.quiz_a, "Checkpoint A"),
|
||
markdown_cell(long_lecture_part_three(spec)),
|
||
reflection_cell(
|
||
"Write the current circuit story in your own words. Which part still feels vague or unstable?"
|
||
),
|
||
markdown_cell(long_lecture_part_five(spec)),
|
||
markdown_cell(long_lecture_part_six(spec)),
|
||
markdown_cell(variation_lab_markdown(spec)),
|
||
variation_lab_cell(spec),
|
||
reflection_cell(
|
||
"After the variation lab, explain which code change most clearly altered the diagram and whether the behavioral result matched your prediction."
|
||
),
|
||
quiz_cell(spec.quiz_b, "Checkpoint B"),
|
||
reflection_cell(
|
||
"Describe one design change you would now try, what you predict it would do, and why."
|
||
),
|
||
markdown_cell(long_lecture_part_seven(spec)),
|
||
markdown_cell(long_lecture_part_nine(spec)),
|
||
markdown_cell(long_lecture_part_ten(spec)),
|
||
markdown_cell(long_lecture_part_eleven(spec)),
|
||
markdown_cell(long_lecture_part_twelve(spec)),
|
||
markdown_cell(mastery_markdown(spec)),
|
||
]
|
||
return notebook(cells)
|
||
|
||
|
||
def build_start_here() -> dict:
|
||
cells = [
|
||
markdown_cell(
|
||
"""
|
||
# Start Here
|
||
|
||
This notebook is the only supported starting point for the course. Its job is to tell you how to use the platform so that the later notebooks do not feel like disconnected technical fragments. The platform is intended to function like a notebook-based textbook plus lab manual plus design studio. If you only click through cells, you will get much less from it than the structure is designed to offer.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## How To Read The Course
|
||
|
||
Every serious notebook in this repository is built around the same teaching loop:
|
||
|
||
- extended lecture notes explain the concept and its professional purpose
|
||
- multiple choice quizzes force retrieval instead of passive recognition
|
||
- editable labs force you to alter the code and watch the diagram respond
|
||
- analysis cells force you to interpret what you changed
|
||
- a mastery gate tells you what ability you are actually supposed to own
|
||
|
||
This is deliberate. The course is trying to train design reasoning, not just tool familiarity.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Label Contract
|
||
|
||
The walkthrough now uses one explicit contract:
|
||
|
||
- `META` cells are about route, objectives, pacing, or how to use the notebook
|
||
- `MANDATORY` cells are the official end-to-end walkthrough
|
||
- `FACULTATIVE` cells are optional extensions for deeper study or consolidation
|
||
|
||
Every labeled cell also carries a difficulty from `1` to `10`. The numbers `1`, `2`, and `3` are reserved for mandatory cells. Higher numbers belong only to facultative extensions.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## What To Do In Your First Session
|
||
|
||
Read the rest of this notebook, then open `COURSE_BLUEPRINT.ipynb`. That is the first serious orientation notebook in the enforced mainline path. After that, follow the notebook-to-notebook handoff from inside the notebooks themselves instead of guessing from the filesystem.
|
||
"""
|
||
),
|
||
code_cell(SETUP_CELL),
|
||
code_cell(
|
||
"""
|
||
from quantum_learning import load_curriculum, load_mastery_blueprint
|
||
from quantum_learning.interactive import quiz_block
|
||
|
||
blueprint = load_mastery_blueprint()
|
||
curriculum = load_curriculum()
|
||
|
||
{
|
||
"terminal_role": blueprint.terminal_role,
|
||
"stage_count": len(blueprint.stages),
|
||
"lesson_count": len(curriculum),
|
||
}
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## The Structure You Are Entering
|
||
|
||
The course does not imagine that you become a master circuit designer by finishing five gentle notebooks. It imagines that you become a master only after passing through several distinct identities: beginner, circuit-literate builder, state-and-measurement thinker, composable designer, synthesis engineer, hardware-aware optimizer, verifier, and finally capstone designer.
|
||
|
||
That sequence matters because expertise is layered. Each stage gives you a new way to look at circuits. At the beginning you mostly see syntax. Then you start seeing processes. Later you start seeing patterns. Later still you see constraints, tradeoffs, failure modes, and review language. This course is trying to make that transformation explicit rather than accidental.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Why The Platform Is Becoming More Literary And More Interactive
|
||
|
||
You asked for accompanying literature inside the notebooks because opening bare code felt disorienting. That diagnosis was correct. A serious self-study platform cannot assume that the learner already knows which sentence, diagram, or output matters most. The platform therefore now treats writing as part of the engineering infrastructure. The text tells you what problem is being solved, what distinction matters, what confusion to avoid, and what ability the notebook is meant to install.
|
||
|
||
But text alone is not enough either. Good pedagogy alternates explanation with retrieval and manipulation. That is why the notebooks now combine longer lecture notes with multiple choice quizzes and editable labs. Reading tells you what the designers want you to notice. Quizzes test whether that notice has become recall. Editable labs test whether recall survives contact with variation. This is the core instructional triangle of the platform.
|
||
"""
|
||
),
|
||
markdown_cell(start_here_extended_reading_one()),
|
||
markdown_cell(
|
||
"""
|
||
## How To Judge Whether A Notebook Worked
|
||
|
||
The right metric is not “did I finish the notebook?” The right metric is “what can I now explain or modify that I could not explain or modify before?” That is why each notebook has a mastery gate. The gate is the honest end-point of the lesson. If the gate still feels unstable, you have not failed; you have simply learned what still needs consolidation.
|
||
|
||
Use the reflection prompts seriously. They are there to turn vague discomfort into precise diagnosis. “I am lost” is not yet a useful learning statement. “I can draw the circuit, but I still cannot explain why that gate changes the support pattern” is useful. The course is trying to move you from the first kind of statement to the second.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## What To Do When You Feel Lost
|
||
|
||
Feeling lost is not itself a problem. Staying vague about why you feel lost is the problem. The platform is trying to give you tools for that diagnosis. If a notebook overwhelms you, stop and ask: is the problem vocabulary, code syntax, diagram reading, prediction, or interpretation? Those are different failures and they need different repairs. A well-designed notebook should help you name which one is happening.
|
||
|
||
This is also why the notebooks now include reflection boxes. They are not decorative journaling. They are structured places to write the sentence you currently cannot yet say fluently. Often the missing sentence is the actual missing concept. Once you can name the unstable point, later study becomes much more efficient.
|
||
"""
|
||
),
|
||
markdown_cell(start_here_extended_reading_two()),
|
||
markdown_cell(
|
||
"""
|
||
## Why The Platform Uses Multiple Choice Quizzes
|
||
|
||
Many technically strong learners underestimate the value of retrieval. They assume that because a paragraph made sense while reading it, the idea is already stable. It usually is not. A multiple choice question is useful not because it is the highest form of assessment, but because it exposes whether the concept is available without the notebook whispering the answer at you.
|
||
|
||
That matters especially in self-study. A quiz can reveal confusion immediately and cheaply. If you miss a question, the point is not that you failed. The point is that the notebook just showed you exactly where the explanation must be reread or rephrased.
|
||
"""
|
||
),
|
||
meta_quiz_cell(
|
||
[
|
||
{
|
||
"prompt": "What is the main purpose of the markdown in these notebooks?",
|
||
"options": [
|
||
"It is mostly decorative context around the code",
|
||
"It carries part of the actual course content and must be studied",
|
||
"It exists only so the notebooks look more polished",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "The markdown is part of the teaching, not decoration.",
|
||
},
|
||
{
|
||
"prompt": "Why does the course emphasize prediction before execution?",
|
||
"options": [
|
||
"Because prediction is the fastest way to run a notebook",
|
||
"Because prediction reveals whether you actually understand the circuit",
|
||
"Because prediction removes the need for simulation",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "Prediction turns passive reading into active reasoning.",
|
||
},
|
||
{
|
||
"prompt": "What should you open immediately after this notebook?",
|
||
"options": [
|
||
"The capstone review notebook",
|
||
"The README again",
|
||
"COURSE_BLUEPRINT.ipynb",
|
||
],
|
||
"correct_index": 2,
|
||
"explanation": "Course Blueprint is the next required notebook in the guarded mainline path.",
|
||
},
|
||
],
|
||
"Start Here Quiz",
|
||
),
|
||
meta_quiz_cell(
|
||
[
|
||
{
|
||
"prompt": "What should you do if a notebook feels dense or overwhelming?",
|
||
"options": [
|
||
"Skim the markdown and hope later notebooks clarify it",
|
||
"Diagnose the exact friction point and use the interactive elements to test that weakness",
|
||
"Skip directly to advanced notebooks with more exciting topics",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "Precise diagnosis is much more useful than vague frustration.",
|
||
},
|
||
{
|
||
"prompt": "Why are editable labs important in this course?",
|
||
"options": [
|
||
"They test whether your explanation survives deliberate circuit changes",
|
||
"They mainly make the notebooks look modern",
|
||
"They replace the need for written explanation",
|
||
],
|
||
"correct_index": 0,
|
||
"explanation": "Manipulation is one of the main ways the notebooks check for real understanding.",
|
||
},
|
||
{
|
||
"prompt": "What does the course expect from foundational notebooks?",
|
||
"options": [
|
||
"Fast completion",
|
||
"Respectful, detailed study because fundamentals shape later design judgment",
|
||
"Minimal attention because they are mostly syntax",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "The course treats fundamentals as the place where professional habits begin.",
|
||
},
|
||
],
|
||
"Start Here Quiz B",
|
||
),
|
||
code_cell(
|
||
"""
|
||
from quantum_learning.interactive import reflection_box
|
||
|
||
reflection_box("Write down what you expect a professional circuit designer to be able to do that you cannot yet do.")
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## How To Continue After This Notebook
|
||
|
||
After this notebook, open `COURSE_BLUEPRINT.ipynb`. Then follow the explicit notebook-to-notebook handoff through the mainline path. Read the text, answer the quizzes, edit the circuit lab, and use the reflection prompts. That full cycle is now the expected workflow. If you only sample one of those pieces, you are no longer using the notebook in the way it was designed to teach.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Final Instruction
|
||
|
||
Open `COURSE_BLUEPRINT.ipynb` next. Then follow the mainline handoff from notebook to notebook. Do not branch away from that route on the first run. The notebooks are now written to be read in detail, quizzed actively, and modified interactively. That is the intended workflow.
|
||
"""
|
||
),
|
||
]
|
||
return notebook(cells)
|
||
|
||
|
||
def build_professional_path() -> dict:
|
||
cells = [
|
||
markdown_cell(
|
||
"""
|
||
# Professional Path
|
||
|
||
This notebook is the hidden didactical map of the course. It is not part of the supported consumer route. Its purpose is to answer the question: what must be learned, mastered, and defended at every stage if the final goal is genuine professional circuit design rather than superficial tool familiarity?
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## The Backward-Design Principle
|
||
|
||
The course is built backward from the terminal capability. That terminal capability is not “knows Qiskit,” not “can run example notebooks,” and not “has seen Bell states.” The terminal capability is: **independent hardware-aware circuit designer who can propose, compare, debug, and defend circuit architectures under constraints.**
|
||
|
||
Once that target is stated clearly, the earlier stages stop being arbitrary. Beginner work is no longer something you “have to get through.” It becomes the deliberate installation of subskills without which later engineering judgment collapses. This is why the course begins with literacy, state reasoning, and measurement reasoning, even though the long-term target is much more advanced.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## The Stage Ladder
|
||
|
||
The stage ladder should be read as a sequence of identities, not just a sequence of topics. At each stage, the question is: what kind of person are you becoming as a designer?
|
||
|
||
You begin as someone who cannot yet parse a diagram confidently. Then you become someone who can narrate small circuits. Then someone who can predict state evolution. Then someone who can think in reusable blocks. Then someone who can synthesize designs, adapt them to constraints, reason about noise, review failures, and finally defend a capstone design. That is a dramatic transformation. The course should feel structured enough to support it.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Why The Ladder Is Emotional As Well As Technical
|
||
|
||
Many learners experience a hidden emotional problem when studying advanced technical material: they cannot tell whether confusion means the subject is deep, the material is poorly taught, or they themselves are missing a prerequisite. A well-articulated stage ladder helps with that. It gives your confusion a location. If you cannot yet think in reusable subcircuits, that does not mean you are bad at quantum computing in general. It means you are at a specific stage boundary. That is a much healthier and more useful diagnosis.
|
||
|
||
Good pedagogy respects this emotional dimension without becoming soft. The course still expects real work. But it tries to make that work legible. Legibility is one of the reasons the notebooks are becoming more text-heavy and more explicit about objectives, gates, and deliverables.
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Why The Technical Notebooks Must Feel Denser Than Normal Tutorials
|
||
|
||
A normal tutorial is optimized for quick success. That is useful when the goal is tool onboarding. It is not enough when the goal is professional circuit design. Professional growth requires deeper reading, repeated explanation, retrieval, self-diagnosis, and gradual comfort with ambiguity. That is why the notebooks are being rewritten with much heavier explanatory text than a standard example-driven notebook would contain.
|
||
|
||
The text is not there because the code is weak. The text is there because code by itself often hides the conceptual burden. A line of code can be syntactically simple and conceptually deep. Without explanation, beginners tend to either overestimate or underestimate the importance of a line. Dense lecture writing helps calibrate attention. The quizzes and reflection prompts then test whether that calibration has stuck.
|
||
"""
|
||
),
|
||
markdown_cell(professional_path_extended_reading_one()),
|
||
markdown_cell(
|
||
"""
|
||
## How To Use The Interactive Elements In The Technical Notebooks
|
||
|
||
The quizzes are for retrieval. The editable labs are for manipulation. The reference tables are for code-to-diagram translation. The reflection boxes are for self-explanation. Those are different learning mechanisms, and they complement each other. If you use all four, the notebook behaves much more like a live teaching environment. If you use only one, the notebook shrinks back toward a static document.
|
||
|
||
This matters because circuit design expertise is not a single skill. It is an integration of language, prediction, manipulation, and judgment. The platform therefore has to exercise all of those muscles, not merely one of them.
|
||
"""
|
||
),
|
||
markdown_cell(professional_path_extended_reading_two()),
|
||
markdown_cell(
|
||
"""
|
||
## The Real Backward Calculation
|
||
|
||
If the capstone requires you to defend a design review, then you must already know how to compare candidates. If you must compare candidates, you must already know how to describe circuit structure. If you must describe circuit structure, you must already know how to read code and diagrams without confusion. If you must debug noise and constraint effects later, you must already have clean ideal intuitions now. This is the backward calculation in its simplest form: every advanced ability decomposes into earlier abilities, and every earlier ability must be trained deliberately if the final role is serious.
|
||
|
||
That is why the course does not apologize for starting simply. The simplicity is not the endpoint. It is the first stable platform from which the later complexity can be climbed.
|
||
"""
|
||
),
|
||
code_cell(SETUP_CELL),
|
||
code_cell(
|
||
"""
|
||
from quantum_learning import load_curriculum, load_mastery_blueprint
|
||
from quantum_learning.interactive import quiz_block
|
||
|
||
blueprint = load_mastery_blueprint()
|
||
curriculum = load_curriculum()
|
||
|
||
[(stage.order, stage.title, stage.identity, stage.gate) for stage in blueprint.stages]
|
||
"""
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## How To Use The Ladder
|
||
|
||
Do not use the stage ladder as a motivational poster. Use it as a diagnostic instrument. Ask, at each stage, whether you can already perform the gate condition of that stage. If not, identify the missing subskill precisely. Vague self-descriptions like “I’m still a beginner” are not useful enough. More useful statements are things like “I cannot yet predict measurement-basis effects before running a circuit” or “I still flatten every design into raw gate sequences instead of reusable blocks.” The notebook is trying to push you toward that higher-resolution self-assessment.
|
||
"""
|
||
),
|
||
code_cell(
|
||
"""
|
||
[(lesson.stage, lesson.title, lesson.deliverable, lesson.mastery_gate) for lesson in curriculum]
|
||
"""
|
||
),
|
||
meta_quiz_cell(
|
||
[
|
||
{
|
||
"prompt": "Why is the course built backward from the capstone role?",
|
||
"options": [
|
||
"Because advanced notebooks are easier to write first",
|
||
"Because the terminal professional capability determines which prerequisites actually matter",
|
||
"Because it removes the need for fundamentals",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "Backward design tells us which earlier capabilities are genuinely necessary.",
|
||
},
|
||
{
|
||
"prompt": "What is a mastery gate for?",
|
||
"options": [
|
||
"To punish the learner for moving too slowly",
|
||
"To check whether a notebook file exists",
|
||
"To state the concrete ability that marks real progress",
|
||
],
|
||
"correct_index": 2,
|
||
"explanation": "A mastery gate defines the real competence to be owned.",
|
||
},
|
||
{
|
||
"prompt": "What is the difference between a topic list and an apprenticeship path?",
|
||
"options": [
|
||
"An apprenticeship path changes who the learner can be as a designer",
|
||
"A topic list always uses more notebooks",
|
||
"An apprenticeship path avoids all quizzes",
|
||
],
|
||
"correct_index": 0,
|
||
"explanation": "The apprenticeship model is about identity and capability growth, not just exposure.",
|
||
},
|
||
],
|
||
"Professional Path Quiz A",
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## What You Should Feel After Reading This Notebook
|
||
|
||
You should not feel that the path is easy. You should feel that the path is finally legible. A serious course does not pretend that mastery comes cheaply. It tries to make the staircase visible, so that each step can be attacked honestly and deliberately. That is the role of this notebook. Once you understand the staircase, the opening technical notebooks should feel like the first rungs of a long but coherent climb.
|
||
"""
|
||
),
|
||
code_cell(
|
||
"""
|
||
from quantum_learning.interactive import reflection_box
|
||
|
||
reflection_box("List the three most important professional subskills you are currently missing.")
|
||
"""
|
||
),
|
||
meta_quiz_cell(
|
||
[
|
||
{
|
||
"prompt": "Which statement is closest to the terminal goal of the course?",
|
||
"options": [
|
||
"Be able to run Qiskit notebooks locally",
|
||
"Become an independent hardware-aware circuit designer",
|
||
"Memorize the major gate families",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "The course aims much higher than local execution literacy.",
|
||
},
|
||
{
|
||
"prompt": "How should you interpret beginner notebooks after reading this notebook?",
|
||
"options": [
|
||
"As trivial material to finish quickly",
|
||
"As prerequisites that install the language and mental models for later design work",
|
||
"As optional supplements that can be skipped",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "The foundational notebooks are prerequisites for later engineering judgment.",
|
||
},
|
||
{
|
||
"prompt": "What is the next action after this notebook?",
|
||
"options": [
|
||
"Start the technical sequence and study it with prediction, quizzes, and editable labs",
|
||
"Jump directly to the capstone notebook",
|
||
"Ignore the quizzes because the text is enough",
|
||
],
|
||
"correct_index": 0,
|
||
"explanation": "The technical sequence is where the blueprint turns into training.",
|
||
},
|
||
],
|
||
"Professional Path Quiz B",
|
||
),
|
||
meta_quiz_cell(
|
||
[
|
||
{
|
||
"prompt": "What changes most across the course stages?",
|
||
"options": [
|
||
"Only the number of gates in the examples",
|
||
"The unit of thought, from local syntax toward patterns, constraints, and tradeoffs",
|
||
"Only the notebook length",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "The course is designed to change how you see and reason about circuits, not just how many examples you have seen.",
|
||
},
|
||
{
|
||
"prompt": "Why is backward design valuable for an ambitious learner?",
|
||
"options": [
|
||
"It translates ambition into visible prerequisite capabilities",
|
||
"It removes the need for staged practice",
|
||
"It guarantees that advanced material will feel easy",
|
||
],
|
||
"correct_index": 0,
|
||
"explanation": "Backward design turns a distant goal into a concrete prerequisite ladder.",
|
||
},
|
||
{
|
||
"prompt": "What is a healthy interpretation of being at an early stage?",
|
||
"options": [
|
||
"It means advanced work is irrelevant",
|
||
"It means you are missing specific subskills that can be named and trained",
|
||
"It means you should avoid self-explanation until later",
|
||
],
|
||
"correct_index": 1,
|
||
"explanation": "Stage language is meant to improve diagnosis, not to freeze identity.",
|
||
},
|
||
],
|
||
"Professional Path Quiz C",
|
||
),
|
||
markdown_cell(
|
||
"""
|
||
## Your Responsibility As The Learner
|
||
|
||
The platform can provide structure, explanations, and interaction, but it cannot do the decisive act for you: honest engagement. That means answering the quizzes without peeking, editing the labs rather than merely admiring them, and writing reflections precise enough that another engineer could understand what you currently do and do not know. If you do that, the notebooks become apprenticeship tools. If you do not, they collapse back into passive reading material.
|
||
"""
|
||
),
|
||
]
|
||
return notebook(cells)
|
||
|
||
|
||
TECHNICAL_SPECS = [
|
||
TechnicalSpec(
|
||
filename="00_circuit_literacy.ipynb",
|
||
title="Circuit Literacy",
|
||
learning_objective="read small circuits fluently, narrate each step, and connect code structure to diagram structure without confusion",
|
||
professional_relevance="Every later skill in the course assumes that you can glance at a small circuit and recover its structure without cognitive panic. If that literacy is weak, statevector reasoning, transpilation reasoning, and debugging all become much harder than they need to be.",
|
||
key_vocabulary=[
|
||
("quantum register", "the set of qubit wires carrying quantum state through the circuit"),
|
||
("classical register", "the place where measurement outcomes are stored"),
|
||
("control", "the qubit whose value conditions a controlled gate"),
|
||
("target", "the qubit modified when the control condition is satisfied"),
|
||
],
|
||
mental_models=[
|
||
"A circuit is a time-ordered process, not a static picture.",
|
||
"Measurement is the conversion from quantum evolution to classical record.",
|
||
"A gate comment should explain an intention, not merely restate syntax.",
|
||
],
|
||
canonical_story="prepare superposition on one qubit, correlate the second qubit with it, and finally turn the resulting quantum pattern into classical data through measurement",
|
||
design_lens="Look for structure first: preparation, correlation, and readout are distinct design roles.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(2, 2, name="bell_literacy")
|
||
# [1] prepare a balanced superposition on qubit 0
|
||
circuit.h(0)
|
||
# [2] use qubit 0 to correlate qubit 1
|
||
circuit.cx(0, 1)
|
||
# [3] convert the quantum pattern into classical data
|
||
circuit.measure([0, 1], [0, 1])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Apply H to qubit 0", "diagram_effect": "Create the first branching point on the top wire", "why_it_matters": "This is the preparation step that makes later correlation nontrivial."},
|
||
{"marker": "[2]", "code_focus": "Apply CX from qubit 0 to qubit 1", "diagram_effect": "Link the wires with a controlled interaction", "why_it_matters": "This is where the second qubit starts depending on the first."},
|
||
{"marker": "[3]", "code_focus": "Measure both qubits", "diagram_effect": "Terminate both quantum wires into classical bits", "why_it_matters": "This is the interface between ideal state preparation and observed data."},
|
||
],
|
||
analysis_code="""
|
||
from quantum_learning.experiments import bell_circuit
|
||
from quantum_learning.visualization import draw_circuit
|
||
|
||
starter = bell_circuit()
|
||
draw_circuit(starter)
|
||
starter.count_ops(), starter.depth()
|
||
""",
|
||
analysis_caption="Use this cell to compare the editable version you built with the packaged Bell helper. The goal is not to admire the helper. The goal is to check whether you recognize the same structure in both forms.",
|
||
common_mistakes=[
|
||
"confusing classical bits with qubits",
|
||
"treating measurement as just another reversible gate",
|
||
"reading the diagram as decoration rather than as an ordered process",
|
||
],
|
||
heuristics=[
|
||
"Narrate each gate in plain language before you trust your understanding.",
|
||
"Ask which lines are preparing state and which lines are merely recording it.",
|
||
"If you cannot point to the controlled interaction, you have not really read the circuit yet.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is the main role of the Hadamard in the canonical Bell circuit?", "options": ["It stores the measurement result", "It creates the initial superposition that later gets correlated", "It swaps the two qubits"], "correct_index": 1, "explanation": "The Hadamard prepares the branching structure that the controlled gate can exploit."},
|
||
{"prompt": "What does the CX add to the circuit story?", "options": ["A correlation between the qubits", "A classical memory cell", "A guarantee that both outputs are 1"], "correct_index": 0, "explanation": "The CX links the second qubit’s behavior to the first qubit."},
|
||
{"prompt": "Why are classical bits present in this version of the circuit?", "options": ["Because measurement needs somewhere to write outcomes", "Because every circuit must have as many classical bits as qubits", "Because classical bits increase entanglement"], "correct_index": 0, "explanation": "Measurement writes observed outcomes into classical storage."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "If you removed the measurements, what would change first?", "options": ["The internal quantum preparation steps would disappear", "The classical readout layer would disappear", "The qubit count would become zero"], "correct_index": 1, "explanation": "Removing measurements removes the readout layer, not the preparation logic."},
|
||
{"prompt": "Why do the code comments use numbered markers?", "options": ["To connect code lines to diagram effects and explanations", "To satisfy Qiskit syntax", "To improve simulation speed"], "correct_index": 0, "explanation": "The markers explicitly connect code, diagram, and narrative."},
|
||
{"prompt": "What is the best reading habit for this notebook?", "options": ["Run all cells quickly", "Predict and narrate each step before executing", "Skip the markdown if the diagram looks familiar"], "correct_index": 1, "explanation": "Prediction and narration build the actual skill this notebook targets."},
|
||
],
|
||
mastery_gate="Take a three-line two-qubit circuit, explain every gate’s structural role, redraw the story in words, and modify one gate without losing track of what changed conceptually.",
|
||
stretch_prompts=[
|
||
"Insert a barrier and explain what changed structurally and what did not.",
|
||
"Swap the control and target qubits and predict the effect before rendering.",
|
||
"Remove measurement, then write the new circuit story without mentioning classical data.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="01_statevectors.ipynb",
|
||
title="Statevectors",
|
||
learning_objective="reason about the internal ideal state of a small circuit before any measurement occurs",
|
||
professional_relevance="Designers do not merely inspect measurement outcomes. They also reason about the internal state the circuit is trying to create. Statevector thinking is the clean-room version of that skill.",
|
||
key_vocabulary=[
|
||
("amplitude", "the complex coefficient associated with a basis state"),
|
||
("probability support", "the set of basis states carrying nonzero probability"),
|
||
("statevector", "the ideal pure-state representation before measurement"),
|
||
("basis state", "a computational-basis label like 00 or 11"),
|
||
],
|
||
mental_models=[
|
||
"The statevector is the internal ideal story before collapse.",
|
||
"Probabilities come from amplitudes; they are not the same object.",
|
||
"Sparse support usually reflects real structural features of the circuit.",
|
||
],
|
||
canonical_story="build a Bell state without measurement so the internal state can be inspected before it is turned into sampled classical outcomes",
|
||
design_lens="Ask which basis states should carry weight, and which should vanish because of the circuit structure.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
from qiskit.quantum_info import Statevector
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
from quantum_learning.utils import statevector_probabilities
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(2, name="bell_state")
|
||
# [1] create superposition on the first qubit
|
||
circuit.h(0)
|
||
# [2] spread the correlation to the second qubit
|
||
circuit.cx(0, 1)
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Hadamard on qubit 0", "diagram_effect": "Open two coherent branches on the first wire", "why_it_matters": "Without superposition there is no interesting pure-state structure to inspect."},
|
||
{"marker": "[2]", "code_focus": "Controlled X onto qubit 1", "diagram_effect": "Turn single-qubit branching into two-qubit correlation", "why_it_matters": "This is why probability support lands on correlated basis states."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit.quantum_info import Statevector
|
||
from quantum_learning.utils import statevector_probabilities
|
||
from quantum_learning.visualization import plot_probabilities
|
||
|
||
bell = QuantumCircuit(2, name="bell_state")
|
||
bell.h(0)
|
||
bell.cx(0, 1)
|
||
|
||
statevector = Statevector.from_instruction(bell)
|
||
probabilities = statevector_probabilities(bell)
|
||
statevector.data, probabilities
|
||
plot_probabilities(probabilities, title="Bell-state probabilities")
|
||
""",
|
||
analysis_caption="This analysis cell shows both the raw statevector coefficients and the derived probabilities. The teaching point is to keep those two descriptions distinct while understanding how one induces the other.",
|
||
common_mistakes=[
|
||
"thinking amplitudes are already probabilities",
|
||
"forgetting that statevectors describe the pre-measurement ideal story",
|
||
"looking at zero-probability basis states without asking which circuit feature suppressed them",
|
||
],
|
||
heuristics=[
|
||
"Name the expected support before you inspect the statevector.",
|
||
"Translate raw amplitudes into a story about which basis states matter.",
|
||
"Whenever a basis state has zero weight, ask what structural reason forced it out.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What does the statevector describe in this notebook?", "options": ["The classical counts after measurement", "The ideal internal state before measurement", "The coupling map of the backend"], "correct_index": 1, "explanation": "The statevector is the clean internal representation before collapse."},
|
||
{"prompt": "Why are amplitudes not the same as probabilities?", "options": ["Because amplitudes can be complex and probabilities are derived from them", "Because probabilities only exist for three-qubit systems", "Because amplitudes are classical"], "correct_index": 0, "explanation": "Probabilities come from amplitude magnitudes, not from the raw amplitude values themselves."},
|
||
{"prompt": "Why is Bell-state support concentrated on only two basis states?", "options": ["Because the circuit structure correlates the two qubits", "Because the simulator dropped the missing states by accident", "Because two-qubit circuits can only use two basis states"], "correct_index": 0, "explanation": "The support pattern is a structural consequence of the preparation and entangling steps."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is the design lens for this notebook?", "options": ["Count how many gates are in the circuit", "Ask which basis states should carry weight and why", "Avoid using measurement forever"], "correct_index": 1, "explanation": "The notebook trains support-based reasoning."},
|
||
{"prompt": "If you added measurement too early, what would you lose pedagogically?", "options": ["The ability to inspect the clean ideal state directly", "The ability to draw the circuit", "The number of qubits"], "correct_index": 0, "explanation": "Measurement hides the internal pure-state picture."},
|
||
{"prompt": "What is the right reading move after seeing the probabilities?", "options": ["Accept them and move on", "Explain why those exact states and not others received weight", "Assume all entangled states look the same"], "correct_index": 1, "explanation": "Interpretation, not mere observation, is the point."},
|
||
],
|
||
mastery_gate="Given a small circuit without measurement, predict its support pattern, inspect the statevector, and explain the agreement or mismatch precisely.",
|
||
stretch_prompts=[
|
||
"Insert an extra single-qubit rotation before the entangling gate and predict how the support changes.",
|
||
"Replace the entangling gate with a different controlled structure and compare the support.",
|
||
"Add a final measurement only after you have written the pre-measurement state story.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="02_measurement_and_counts.ipynb",
|
||
title="Measurement and Counts",
|
||
learning_objective="connect ideal probabilities to finite-shot classical data and reason about basis-dependent measurement choices",
|
||
professional_relevance="Experimental work and most realistic simulation workflows hand you counts, not pure-state coefficients. A designer has to translate between the clean internal story and the empirical record.",
|
||
key_vocabulary=[
|
||
("shot", "one execution of the circuit ending in one classical outcome"),
|
||
("counts", "the frequency table of observed bitstrings across shots"),
|
||
("basis choice", "the effective question asked by the measurement setup"),
|
||
("empirical distribution", "the probability estimate derived from finite samples"),
|
||
],
|
||
mental_models=[
|
||
"Counts are samples, not exact theoretical probabilities.",
|
||
"Measurement basis is a design choice, often implemented by pre-measurement gates.",
|
||
"Finite fluctuations are normal and do not automatically indicate a design bug.",
|
||
],
|
||
canonical_story="sample a Bell circuit repeatedly, then compare what finite classical data tells you to what the ideal probability picture would predict",
|
||
design_lens="Always ask whether a surprising histogram comes from the state itself, the basis, or finite sampling.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
from quantum_learning.utils import counts_to_probabilities
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(1, 1, name="basis_probe")
|
||
# [1] prepare the |+> state on the qubit
|
||
circuit.h(0)
|
||
# [2] change the question being asked before measurement
|
||
circuit.h(0)
|
||
# [3] record the outcome as a classical bit
|
||
circuit.measure(0, 0)
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare |+> with H", "diagram_effect": "Create a balanced superposition before readout", "why_it_matters": "This makes basis choice pedagogically visible."},
|
||
{"marker": "[2]", "code_focus": "Apply another H before measurement", "diagram_effect": "Rotate the measurement question back into the computational basis", "why_it_matters": "This is how basis choice becomes operational in a circuit."},
|
||
{"marker": "[3]", "code_focus": "Measure into a classical bit", "diagram_effect": "Collapse the process into observed counts", "why_it_matters": "This produces the empirical data the notebook is about."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from quantum_learning.experiments import bell_circuit, simulate_counts
|
||
from quantum_learning.utils import counts_to_probabilities
|
||
from quantum_learning.visualization import plot_counts
|
||
|
||
bell = bell_circuit()
|
||
bell_counts = simulate_counts(bell, shots=512)
|
||
plot_counts(bell_counts, title="Bell measurement counts")
|
||
bell_counts, counts_to_probabilities(bell_counts)
|
||
""",
|
||
analysis_caption="This analysis cell shows the Bell counts and their normalization. The educational move is to look at the gap between exact theory and finite sampling without overreacting to ordinary fluctuations.",
|
||
common_mistakes=[
|
||
"expecting finite counts to match exact fractions perfectly",
|
||
"forgetting that basis changes can be implemented with gates before measurement",
|
||
"blaming every irregularity on a broken circuit instead of checking sample size and basis choice first",
|
||
],
|
||
heuristics=[
|
||
"Normalize counts before comparing them to theoretical probabilities.",
|
||
"Treat basis choice as part of the circuit design, not as a fixed background condition.",
|
||
"If a histogram surprises you, separate sampling effects from design effects before debugging.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is a shot?", "options": ["One gate in a circuit", "One execution of the circuit ending in one outcome", "One qubit in a register"], "correct_index": 1, "explanation": "A shot is one full run of the measured circuit."},
|
||
{"prompt": "Why might balanced theoretical probabilities fail to produce exactly balanced counts?", "options": ["Because finite sampling fluctuates", "Because all balanced circuits are wrong", "Because measurement removes qubits"], "correct_index": 0, "explanation": "Finite-shot data naturally fluctuates around the ideal distribution."},
|
||
{"prompt": "How can a circuit change measurement basis?", "options": ["By editing the README", "By applying gates before the final measurement", "By adding more classical bits"], "correct_index": 1, "explanation": "Basis changes are commonly implemented with pre-measurement transformations."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What should you ask first when a histogram looks surprising?", "options": ["Is the sample size small, is the basis different, or is the design wrong?", "Which color was used in the bar chart?", "How many markdown cells are in the notebook?"], "correct_index": 0, "explanation": "Good interpretation separates sampling, basis, and design effects."},
|
||
{"prompt": "Why normalize counts?", "options": ["To estimate probabilities in a comparable form", "To remove all randomness", "To make the circuit shorter"], "correct_index": 0, "explanation": "Normalized counts become an empirical distribution."},
|
||
{"prompt": "What ability is this notebook trying to install?", "options": ["Local execution without interpretation", "Translation between ideal probability stories and empirical classical records", "Permanent avoidance of measurement"], "correct_index": 1, "explanation": "That translation is the core skill here."},
|
||
],
|
||
mastery_gate="Take a small measured circuit, predict its histogram qualitatively, explain any finite-shot deviations, and describe how a basis change would alter the question being asked.",
|
||
stretch_prompts=[
|
||
"Run the same circuit at several shot counts and compare how the histogram stabilizes.",
|
||
"Remove the pre-measurement basis-change gate and explain the difference in the result.",
|
||
"Design a one-qubit circuit that is deterministic in one basis and balanced in another.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="03_transpilation_and_backend_reality.ipynb",
|
||
title="Transpilation and Backend Reality",
|
||
learning_objective="see how an abstract circuit is rewritten under basis-gate and topology constraints, and interpret the engineering cost of that rewrite",
|
||
professional_relevance="Abstract correctness is only half the engineering story. Real backends impose basis and connectivity constraints that can inflate depth, add routing, and sometimes suggest that the circuit should be redesigned by hand.",
|
||
key_vocabulary=[
|
||
("basis gates", "the primitive gate set allowed by a backend"),
|
||
("coupling map", "the graph of allowed two-qubit interactions"),
|
||
("routing", "the extra transformations needed to move logical interactions onto allowed edges"),
|
||
("transpilation cost", "the depth, size, and structure inflation induced by compilation"),
|
||
],
|
||
mental_models=[
|
||
"The written circuit is a request; the transpiled circuit is the executable answer to that request.",
|
||
"Topology pressure shows up when desired interactions are not local in the coupling graph.",
|
||
"A rewritten circuit can be correct and still be a poor engineering outcome.",
|
||
],
|
||
canonical_story="write a conceptually clear circuit, then watch how a restricted basis and line topology force the implementation to change",
|
||
design_lens="After transpilation, ask what was added, what got decomposed, and which abstract interactions were most expensive to realize.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
from qiskit.providers.basic_provider import BasicSimulator
|
||
|
||
from quantum_learning.experiments import line_coupling_map, transpile_summary
|
||
""",
|
||
editable_context_names=["QuantumCircuit"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(3, name="routing_pressure")
|
||
# [1] prepare a control qubit in superposition
|
||
circuit.h(0)
|
||
# [2] ask for a long-range interaction
|
||
circuit.cx(0, 2)
|
||
# [3] add a local follow-up interaction
|
||
circuit.cx(0, 1)
|
||
# [4] finish with a single-qubit rotation
|
||
circuit.rx(0.37, 2)
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare the control qubit", "diagram_effect": "Create the initial branch structure on the top wire", "why_it_matters": "This establishes the control context for later interactions."},
|
||
{"marker": "[2]", "code_focus": "Long-range CX from 0 to 2", "diagram_effect": "Request a nonlocal interaction across the diagram", "why_it_matters": "This is the source of routing pressure under a line topology."},
|
||
{"marker": "[3]", "code_focus": "Local CX from 0 to 1", "diagram_effect": "Add an interaction that is cheap in the line topology", "why_it_matters": "This contrasts local and nonlocal implementation cost."},
|
||
{"marker": "[4]", "code_focus": "Single-qubit rotation on qubit 2", "diagram_effect": "Add local refinement after the entangling structure", "why_it_matters": "Not every step is equally painful under hardware constraints."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from qiskit.providers.basic_provider import BasicSimulator
|
||
from quantum_learning.experiments import line_coupling_map, transpile_summary
|
||
from quantum_learning.visualization import draw_circuit
|
||
|
||
circuit = QuantumCircuit(3, name="routing_pressure")
|
||
circuit.h(0)
|
||
circuit.cx(0, 2)
|
||
circuit.cx(0, 1)
|
||
circuit.rx(0.37, 2)
|
||
|
||
summary = transpile_summary(
|
||
circuit,
|
||
BasicSimulator(),
|
||
basis_gates=["rz", "sx", "x", "cx"],
|
||
coupling_map=line_coupling_map(3),
|
||
)
|
||
|
||
{key: value for key, value in summary.items() if key != "compiled_circuit"}
|
||
draw_circuit(summary["compiled_circuit"])
|
||
""",
|
||
analysis_caption="This analysis cell exposes depth inflation, size inflation, and the rewritten circuit itself. The task is to explain where the extra structure came from and whether a human redesign might beat the default rewrite.",
|
||
common_mistakes=[
|
||
"assuming abstract correctness means the implementation problem is solved",
|
||
"seeing extra gates after transpilation without asking which constraint forced them in",
|
||
"treating transpilation as black-box magic rather than as a structural response to hardware limits",
|
||
],
|
||
heuristics=[
|
||
"Find the nonlocal interactions first; they often dominate the routing cost.",
|
||
"Compare before and after depth, but also inspect where the new structure appears.",
|
||
"If compilation cost is ugly, consider redesign instead of merely accepting the result.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is transpilation doing in this notebook?", "options": ["Changing the problem into a different algorithm", "Rewriting the circuit to satisfy backend constraints", "Removing all two-qubit gates"], "correct_index": 1, "explanation": "The goal is to produce an executable circuit under explicit constraints."},
|
||
{"prompt": "Why is the CX from qubit 0 to qubit 2 expensive under a line coupling map?", "options": ["Because nonadjacent interactions need routing help", "Because RX gates forbid it", "Because three-qubit circuits cannot use CX"], "correct_index": 0, "explanation": "The interaction is nonlocal with respect to the allowed graph."},
|
||
{"prompt": "What should a designer inspect after transpilation?", "options": ["Only the final bar color", "Depth inflation, size inflation, and where extra structure appeared", "Only whether the notebook still opens"], "correct_index": 1, "explanation": "Interpret the cost structurally, not cosmetically."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is the written circuit best thought of as?", "options": ["A final immutable implementation", "A request that may be rewritten for execution", "A classical histogram"], "correct_index": 1, "explanation": "The abstract circuit is a request, not always the executable end-state."},
|
||
{"prompt": "What is the right response if transpilation looks expensive?", "options": ["Assume the compiler is wrong and stop", "Consider whether the abstract design should be rewritten", "Delete all two-qubit gates"], "correct_index": 1, "explanation": "A poor compiled form can be a design signal."},
|
||
{"prompt": "Why is this notebook a turning point in the course?", "options": ["It introduces the move from ideal stories to constrained engineering reality", "It proves simulators are useless", "It removes the need for state reasoning"], "correct_index": 0, "explanation": "This is the first serious contact with backend-style constraints."},
|
||
],
|
||
mastery_gate="Given an abstract circuit and a constrained topology, explain the compiled rewrite, identify the expensive interactions, and propose at least one human redesign to reduce the cost.",
|
||
stretch_prompts=[
|
||
"Rewrite the abstract circuit to make the long-range interaction less painful before transpilation.",
|
||
"Change the coupling map size and compare the cost profile.",
|
||
"Delete one interaction and predict which inflation source disappears first.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="04_noise_and_robustness.ipynb",
|
||
title="Noise and Robustness",
|
||
learning_objective="compare ideal and noisy behavior and interpret which parts of a design are structurally fragile under error",
|
||
professional_relevance="Professional design does not stop at ideal correctness. It asks how much of the intended behavior survives disturbance and what redesign choices may improve robustness.",
|
||
key_vocabulary=[
|
||
("noise model", "a local simulation device for approximating imperfect execution"),
|
||
("robustness", "how strongly the intended behavior survives disturbance"),
|
||
("readout error", "distortion in the final measurement record"),
|
||
("fragility", "sensitivity of the design to error sources"),
|
||
],
|
||
mental_models=[
|
||
"The ideal story is a target, not a guarantee of observed behavior.",
|
||
"Noise can blur an existing pattern or create qualitatively new wrong outcomes.",
|
||
"Robustness is partly a design property, not just a backend property.",
|
||
],
|
||
canonical_story="run the same circuit in ideal and disturbed modes, then compare not just whether they differ but how they differ",
|
||
design_lens="Look for which wrong outcomes appear or grow, and ask what part of the design made them easy to create.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import build_demo_noise_model, simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(2, 2, name="robustness_probe")
|
||
# [1] prepare a balanced control qubit
|
||
circuit.h(0)
|
||
# [2] correlate the second qubit with the first
|
||
circuit.cx(0, 1)
|
||
# [3] record both outcomes for comparison
|
||
circuit.measure([0, 1], [0, 1])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare the control qubit", "diagram_effect": "Create a balanced branch structure", "why_it_matters": "Noise has something nontrivial to distort only once branching exists."},
|
||
{"marker": "[2]", "code_focus": "Entangle via CX", "diagram_effect": "Create the correlated ideal pattern", "why_it_matters": "This determines which wrong outcomes are most revealing under disturbance."},
|
||
{"marker": "[3]", "code_focus": "Measure both qubits", "diagram_effect": "Turn ideal structure into noisy classical evidence", "why_it_matters": "This is where readout error enters the observed record."},
|
||
],
|
||
analysis_code="""
|
||
from quantum_learning.experiments import bell_circuit, build_demo_noise_model, simulate_counts
|
||
from quantum_learning.utils import counts_to_probabilities
|
||
from quantum_learning.visualization import plot_counts
|
||
import matplotlib.pyplot as plt
|
||
|
||
bell = bell_circuit()
|
||
noise_model = build_demo_noise_model()
|
||
ideal_counts = simulate_counts(bell, shots=1024, seed=21)
|
||
noisy_counts = simulate_counts(
|
||
bell,
|
||
shots=1024,
|
||
seed=21,
|
||
noise_model=noise_model,
|
||
basis_gates=list(noise_model.basis_gates),
|
||
)
|
||
|
||
fig, axes = plt.subplots(1, 2, figsize=(10, 3.5))
|
||
plot_counts(ideal_counts, title="Ideal counts", ax=axes[0])
|
||
plot_counts(noisy_counts, title="Noisy counts", ax=axes[1])
|
||
fig.tight_layout()
|
||
|
||
{
|
||
"ideal": counts_to_probabilities(ideal_counts),
|
||
"noisy": counts_to_probabilities(noisy_counts),
|
||
}
|
||
""",
|
||
analysis_caption="This comparison is not just about seeing that noisy output is worse. It is about learning how to read the pattern of distortion and connect it to structural fragility in the underlying design.",
|
||
common_mistakes=[
|
||
"thinking any noisy degradation automatically means the circuit design is bad",
|
||
"looking only at whether performance dropped instead of how the error pattern changed",
|
||
"treating the local noise model as perfect hardware truth instead of as a teaching approximation",
|
||
],
|
||
heuristics=[
|
||
"Compare ideal and noisy support, not just aggregate success rates.",
|
||
"Ask which incorrect outcomes gained weight and why those particular paths are easy to enter.",
|
||
"Use noise to diagnose fragility, not merely to complain about realism.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is the central question of this notebook?", "options": ["Does noise exist?", "How does the pattern of behavior change when disturbance is introduced?", "Can we avoid measurement forever?"], "correct_index": 1, "explanation": "The notebook is about interpreting distortion, not just acknowledging it."},
|
||
{"prompt": "Why compare ideal and noisy runs of the same circuit?", "options": ["To isolate the effect of disturbance from the intended structure", "To make the notebook longer", "To remove the need for theory"], "correct_index": 0, "explanation": "Holding the circuit fixed isolates the environmental effect."},
|
||
{"prompt": "What is a fragile design feature?", "options": ["A feature whose intended behavior degrades easily under error", "Any feature written in Python", "A feature with exactly one qubit"], "correct_index": 0, "explanation": "Fragility is sensitivity to disturbance."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What should you inspect first in a noisy histogram?", "options": ["Which wrong outcomes appeared or grew", "Only the notebook title", "Whether the bars are centered"], "correct_index": 0, "explanation": "The shape of the distortion matters."},
|
||
{"prompt": "Is the local noise model a perfect replica of hardware reality?", "options": ["Yes, exactly", "No, it is a teaching approximation", "Only for Bell states"], "correct_index": 1, "explanation": "The model is useful pedagogically, not exact physically."},
|
||
{"prompt": "Why is robustness partly a design concern?", "options": ["Because some designs expose more vulnerable pathways than others", "Because only the compiler matters", "Because measurement creates robustness automatically"], "correct_index": 0, "explanation": "Design choices affect error sensitivity."},
|
||
],
|
||
mastery_gate="Compare ideal and noisy behavior for a small circuit, identify the most revealing distortion pattern, and explain which structural choices are likely contributing to fragility.",
|
||
stretch_prompts=[
|
||
"Increase readout error and predict which wrong outcomes should grow first.",
|
||
"Simplify the circuit and compare whether the distortion narrows.",
|
||
"Propose one redesign that might improve robustness and justify it before testing.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="05_design_patterns_and_subcircuits.ipynb",
|
||
title="Design Patterns and Subcircuits",
|
||
learning_objective="stop thinking in flat gate lists and start thinking in reusable subcircuits and recurring design motifs",
|
||
professional_relevance="As soon as circuits become larger than toy examples, raw gate sequences become an unreadable maintenance burden. Professionals package ideas into reusable blocks and talk in motifs, not only in primitive operations.",
|
||
key_vocabulary=[
|
||
("subcircuit", "a reusable circuit fragment with a named role"),
|
||
("design pattern", "a recurring structural solution to a familiar circuit need"),
|
||
("composition", "building larger behavior out of smaller named pieces"),
|
||
("abstraction", "hiding low-level detail behind a meaningful circuit interface"),
|
||
],
|
||
mental_models=[
|
||
"A circuit block is to quantum design what a function is to software design.",
|
||
"Readable structure is itself an engineering asset.",
|
||
"If you cannot name a recurring pattern, you will keep rewriting it badly.",
|
||
],
|
||
canonical_story="wrap a familiar entangling motif into a named block and then reuse it inside a larger circuit rather than flattening everything into one long gate list",
|
||
design_lens="Ask whether the circuit is easier to explain, modify, and review once the repeated idea has a name.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
block = QuantumCircuit(2, name="bell_block")
|
||
# [1] define the reusable preparation step
|
||
block.h(0)
|
||
# [2] define the reusable correlation step
|
||
block.cx(0, 1)
|
||
|
||
circuit = QuantumCircuit(3, 3, name="composed_pattern")
|
||
# [3] insert the named block on qubits 0 and 1
|
||
circuit.compose(block, qubits=[0, 1], inplace=True)
|
||
# [4] extend the pattern to a third qubit
|
||
circuit.cx(1, 2)
|
||
# [5] measure the result
|
||
circuit.measure([0, 1, 2], [0, 1, 2])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Define the preparation part of the block", "diagram_effect": "Create the first internal step of the reusable motif", "why_it_matters": "The block should have an intelligible internal story."},
|
||
{"marker": "[2]", "code_focus": "Define the entangling part of the block", "diagram_effect": "Complete the reusable correlated motif", "why_it_matters": "A block needs a stable conceptual purpose to be worth naming."},
|
||
{"marker": "[3]", "code_focus": "Compose the block into the larger circuit", "diagram_effect": "Insert the named motif as a single structural idea", "why_it_matters": "Composition is the bridge from tiny examples to scalable design."},
|
||
{"marker": "[4]", "code_focus": "Extend to a third qubit", "diagram_effect": "Show how a block can seed a larger pattern", "why_it_matters": "Reusable blocks should make extension cleaner, not messier."},
|
||
{"marker": "[5]", "code_focus": "Measure the full circuit", "diagram_effect": "Produce a classical record for the composed design", "why_it_matters": "You still need empirical feedback, even when the structure is abstracted."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from quantum_learning.visualization import draw_circuit
|
||
|
||
def bell_block(name: str = "bell_block"):
|
||
block = QuantumCircuit(2, name=name)
|
||
block.h(0)
|
||
block.cx(0, 1)
|
||
return block
|
||
|
||
block = bell_block()
|
||
composed = QuantumCircuit(3, 3, name="composed_pattern")
|
||
composed.compose(block, qubits=[0, 1], inplace=True)
|
||
composed.cx(1, 2)
|
||
composed.measure([0, 1, 2], [0, 1, 2])
|
||
|
||
draw_circuit(composed)
|
||
""",
|
||
analysis_caption="The analysis cell turns the idea of a reusable block into a concrete circuit drawing. The teaching point is to see how naming and composition change readability without changing the physics you are trying to build.",
|
||
common_mistakes=[
|
||
"assuming abstraction is optional because toy examples are still readable",
|
||
"creating blocks with no stable conceptual purpose",
|
||
"hiding too much structure without being able to explain the block internally",
|
||
],
|
||
heuristics=[
|
||
"Name a block only if you can state its role in one sentence.",
|
||
"Use composition to make larger designs easier to review, not merely shorter to type.",
|
||
"Keep the internal story of the block explainable even after abstraction.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "Why introduce subcircuits at all?", "options": ["To make code harder to read", "To package recurring ideas into reusable, reviewable units", "To avoid using CX gates"], "correct_index": 1, "explanation": "Subcircuits help scale design readability and reuse."},
|
||
{"prompt": "What makes a subcircuit worth naming?", "options": ["It contains at least three gates", "It has a stable conceptual role", "It uses a classical register"], "correct_index": 1, "explanation": "Naming should follow conceptual purpose, not arbitrary size."},
|
||
{"prompt": "What is composition doing in this notebook?", "options": ["Replacing all quantum gates with classical code", "Embedding a named block into a larger design", "Deleting the reusable block"], "correct_index": 1, "explanation": "Composition inserts structured subcircuits into larger circuits."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is the software-design analogy used in this notebook?", "options": ["A subcircuit is like a function", "A qubit is like a comment", "Measurement is like a README"], "correct_index": 0, "explanation": "Subcircuits play a function-like role in managing complexity."},
|
||
{"prompt": "What is a danger of abstraction?", "options": ["It can hide structure that you no longer understand", "It removes the need for diagrams", "It forces the backend to be noiseless"], "correct_index": 0, "explanation": "Abstraction is useful only when the internal story is still understood."},
|
||
{"prompt": "How do you know a block improved the design?", "options": ["The block has a cool name", "The larger circuit becomes easier to explain, modify, and review", "The circuit stops using measurements"], "correct_index": 1, "explanation": "Better abstraction improves design clarity and maintainability."},
|
||
],
|
||
mastery_gate="Refactor a flat circuit into at least one reusable subcircuit, explain the abstraction boundary clearly, and justify why the composed version is a better design artifact.",
|
||
stretch_prompts=[
|
||
"Create two different reusable blocks for the same preparation goal and compare them.",
|
||
"Compose the same block in two different places and explain what stayed invariant.",
|
||
"Write a review note arguing why a certain block boundary is good or bad.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="06_reversible_logic_and_oracles.ipynb",
|
||
title="Reversible Logic and Oracles",
|
||
learning_objective="translate structured logical rules into reversible or oracle-style circuit fragments with explicit control and ancilla reasoning",
|
||
professional_relevance="Many nontrivial circuit constructions are not invented gate by gate. They come from logical rules, predicates, reversible structure, and careful ancilla management. This is a key threshold from beginner examples to algorithmic block engineering.",
|
||
key_vocabulary=[
|
||
("oracle", "a circuit fragment encoding a predicate or marked condition"),
|
||
("ancilla", "an auxiliary qubit used to support a construction"),
|
||
("reversible logic", "logic that preserves enough information to be expressed as a valid quantum transformation"),
|
||
("predicate", "the rule or condition the circuit is supposed to represent"),
|
||
],
|
||
mental_models=[
|
||
"Start from the logical rule, not from random gate search.",
|
||
"Ancillas are resources with purpose and cost, not free decoration.",
|
||
"An oracle is a structured encoding of a question, not a mystical object.",
|
||
],
|
||
canonical_story="begin with a simple logical relation, then implement it as a small reversible or oracle-style circuit whose structure can be justified from the rule itself",
|
||
design_lens="Whenever you add a control or ancilla, ask what exact logical role it is playing.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(3, 3, name="xor_oracle")
|
||
# [1] copy qubit 0 into the target workspace
|
||
circuit.cx(0, 2)
|
||
# [2] fold qubit 1 into the same workspace
|
||
circuit.cx(1, 2)
|
||
# [3] observe all wires for truth-table inspection
|
||
circuit.measure([0, 1, 2], [0, 1, 2])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "First controlled copy into workspace qubit", "diagram_effect": "Make the target reflect qubit 0", "why_it_matters": "This begins the predicate accumulation."},
|
||
{"marker": "[2]", "code_focus": "Second controlled copy into the same workspace", "diagram_effect": "Combine qubit 1 with the stored effect of qubit 0", "why_it_matters": "Together the two controls implement XOR structure."},
|
||
{"marker": "[3]", "code_focus": "Measure all wires", "diagram_effect": "Expose the reversible relation in classical form", "why_it_matters": "This lets you compare the circuit to the truth table directly."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from quantum_learning.visualization import draw_circuit
|
||
|
||
oracle = QuantumCircuit(3, 3, name="xor_oracle")
|
||
oracle.cx(0, 2)
|
||
oracle.cx(1, 2)
|
||
oracle.measure([0, 1, 2], [0, 1, 2])
|
||
|
||
draw_circuit(oracle)
|
||
oracle.count_ops(), oracle.depth()
|
||
""",
|
||
analysis_caption="The analysis cell gives you a compact oracle-style example to compare against a truth table. The teaching move is to make you derive the circuit from the rule instead of treating the circuit as a mysterious pattern found elsewhere.",
|
||
common_mistakes=[
|
||
"starting from gates instead of starting from the logical predicate",
|
||
"adding ancillas or controls without a clearly stated role",
|
||
"calling something an oracle without being able to say what rule it encodes",
|
||
],
|
||
heuristics=[
|
||
"Write the truth table or logical rule before you write the circuit.",
|
||
"For every ancilla, state why it exists and what would break without it.",
|
||
"Treat control structure as a direct encoding of logical dependence.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is the right starting point for oracle design?", "options": ["A random search over gates", "The logical predicate or truth table", "The backend coupling map"], "correct_index": 1, "explanation": "Start from the rule you need to encode."},
|
||
{"prompt": "What is an ancilla?", "options": ["An auxiliary qubit used to support a construction", "A measurement outcome", "A plotting library"], "correct_index": 0, "explanation": "Ancillas are auxiliary resources used for structure."},
|
||
{"prompt": "Why are the two CX gates enough for XOR in this simple example?", "options": ["Because each one folds one input into the workspace qubit", "Because XOR never needs two-qubit gates", "Because measurement computes the XOR for us"], "correct_index": 0, "explanation": "The workspace accumulates the parity information from both controls."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is a danger of calling a circuit an oracle too casually?", "options": ["You may stop explaining what rule it actually represents", "The notebook will not load", "The simulator will become noisy"], "correct_index": 0, "explanation": "Naming should not replace explanation."},
|
||
{"prompt": "What question should you ask about every ancilla?", "options": ["What color is it?", "What exact role does it play and is it necessary?", "How many markdown cells mention it?"], "correct_index": 1, "explanation": "Ancillas need explicit justification."},
|
||
{"prompt": "What ability is this notebook trying to build?", "options": ["Translation from logical rule to reversible circuit structure", "Avoidance of any classical reasoning", "Memorization of one oracle example"], "correct_index": 0, "explanation": "The goal is rule-to-circuit translation."},
|
||
],
|
||
mastery_gate="Start from a clear predicate, build a reversible or oracle-style circuit for it, and justify every control and ancilla in the construction.",
|
||
stretch_prompts=[
|
||
"Replace XOR with a different Boolean rule and derive a new circuit from the truth table.",
|
||
"Add an ancilla intentionally, then argue whether it improved clarity or merely increased cost.",
|
||
"Write a short design memo explaining how your circuit encodes the predicate.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="07_synthesis_and_parametric_design.ipynb",
|
||
title="Synthesis and Parametric Design",
|
||
learning_objective="move from isolated circuits to circuit families, decompositions, and parameterized design choices with explicit tradeoff reasoning",
|
||
professional_relevance="Serious design work often asks for a circuit family, not a single fixed circuit. You need to reason about decomposition, tunable structure, and tradeoffs between clarity, expressivity, and cost.",
|
||
key_vocabulary=[
|
||
("ansatz", "a parameterized circuit family with a chosen structural template"),
|
||
("decomposition", "breaking a desired action into implementable gates or substructures"),
|
||
("expressivity", "how much behavior a parameterized family can represent"),
|
||
("tradeoff", "a deliberate balance between competing engineering goals"),
|
||
],
|
||
mental_models=[
|
||
"A good design often comes as a family, not a one-off artifact.",
|
||
"Every decomposition hides assumptions and costs.",
|
||
"Parameterization is a way to separate structural choice from numerical tuning.",
|
||
],
|
||
canonical_story="start from a simple parameterized template, then treat it as a design family whose structure can be evaluated and compared rather than as a fixed answer",
|
||
design_lens="Ask what the parameters are allowed to change, what the structure keeps fixed, and what engineering cost the family imposes.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(2, 2, name="two_layer_ansatz")
|
||
# [1] set the first local parameter
|
||
circuit.ry(0.2, 0)
|
||
# [2] set the second local parameter
|
||
circuit.ry(-0.3, 1)
|
||
# [3] couple the qubits through an entangling layer
|
||
circuit.cx(0, 1)
|
||
# [4] refine the first qubit
|
||
circuit.rz(0.5, 0)
|
||
# [5] refine the second qubit and measure
|
||
circuit.ry(0.7, 1)
|
||
circuit.measure([0, 1], [0, 1])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "First local parameter", "diagram_effect": "Tune the first qubit's initial orientation", "why_it_matters": "This shows what can change without changing the family structure."},
|
||
{"marker": "[2]", "code_focus": "Second local parameter", "diagram_effect": "Tune the second qubit independently", "why_it_matters": "Parameterized families often begin with local freedom."},
|
||
{"marker": "[3]", "code_focus": "Entangling layer", "diagram_effect": "Introduce shared structure between the qubits", "why_it_matters": "This is usually the expressive heart of the family."},
|
||
{"marker": "[4]", "code_focus": "Post-entanglement refinement", "diagram_effect": "Adjust the first qubit after coupling", "why_it_matters": "Later layers let the family represent richer behavior."},
|
||
{"marker": "[5]", "code_focus": "Final refinement and measurement", "diagram_effect": "Complete the template and expose sampled output", "why_it_matters": "The family only becomes useful once it can be evaluated."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from quantum_learning.visualization import draw_circuit
|
||
|
||
ansatz = QuantumCircuit(2, name="two_layer_ansatz")
|
||
ansatz.ry(0.2, 0)
|
||
ansatz.ry(-0.3, 1)
|
||
ansatz.cx(0, 1)
|
||
ansatz.rz(0.5, 0)
|
||
ansatz.ry(0.7, 1)
|
||
|
||
draw_circuit(ansatz)
|
||
ansatz.count_ops(), ansatz.depth()
|
||
""",
|
||
analysis_caption="The analysis cell turns the parametric template into a visual object you can critique. Ask which aspects of the design are structural and which are merely parameter values that happen to have been chosen here.",
|
||
common_mistakes=[
|
||
"treating one parameter choice as the whole design rather than one point in a family",
|
||
"talking about expressivity without considering depth and cost",
|
||
"calling a decomposition natural without explaining what goal it optimizes",
|
||
],
|
||
heuristics=[
|
||
"Separate the family structure from the chosen parameter values.",
|
||
"When comparing families, ask what each layer is trying to buy you.",
|
||
"Tradeoff language is mandatory: clarity, expressivity, depth, and hardware cost rarely align perfectly.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is an ansatz in this notebook?", "options": ["A parameterized circuit family with a chosen template", "A measurement result", "A fixed backend layout"], "correct_index": 0, "explanation": "An ansatz is a structured family, not just one frozen circuit."},
|
||
{"prompt": "Why is parameterization useful?", "options": ["It separates structural choice from numerical tuning", "It removes the need for entanglement", "It makes all circuits equally expressive"], "correct_index": 0, "explanation": "Parameters let the same structure represent multiple concrete instances."},
|
||
{"prompt": "What should you compare when judging two circuit families?", "options": ["Only the number of markdown cells", "Structure, expressivity, depth, and likely implementation cost", "Only the prettiness of the diagram"], "correct_index": 1, "explanation": "Family comparison is a multidimensional engineering decision."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is the risk of focusing only on one parameter setting?", "options": ["You may mistake one instance for the whole family", "You automatically create a noise model", "You lose the ability to draw the circuit"], "correct_index": 0, "explanation": "The family is the real design object here."},
|
||
{"prompt": "What is a good decomposition question?", "options": ["What goal does this decomposition optimize?", "How fast can I skip it?", "How can I avoid all explanations?"], "correct_index": 0, "explanation": "Every decomposition should be justified by an engineering goal."},
|
||
{"prompt": "What kind of reasoning is being trained?", "options": ["Circuit-family and tradeoff reasoning", "Blind parameter guessing only", "Avoidance of structure"], "correct_index": 0, "explanation": "This notebook tries to make design families legible."},
|
||
],
|
||
mastery_gate="Given a small target behavior, propose a parameterized family or decomposition, explain its structure, and defend the tradeoffs that motivated it.",
|
||
stretch_prompts=[
|
||
"Create a second ansatz family with a different entangling pattern and compare the engineering tradeoffs.",
|
||
"Remove one layer and explain what expressive or design capability was lost.",
|
||
"Write a short review note defending one family over another for a given goal.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="08_hardware_aware_design_studio.ipynb",
|
||
title="Hardware-Aware Design Studio",
|
||
learning_objective="redesign circuits under topology and basis constraints instead of merely accepting compiler output as final truth",
|
||
professional_relevance="This is the point where you stop being only a user of transpilation and begin acting like a hardware-aware designer who can rewrite a circuit intentionally in response to constraints.",
|
||
key_vocabulary=[
|
||
("layout pressure", "the stress introduced when the logical interaction pattern mismatches the physical graph"),
|
||
("manual rewrite", "a human redesign performed before or alongside transpilation"),
|
||
("implementation choice", "the specific route taken to realize an abstract interaction pattern"),
|
||
("constraint-aware design", "designing with backend limits in view from the beginning"),
|
||
],
|
||
mental_models=[
|
||
"The compiler is a collaborator, not an oracle.",
|
||
"If a pattern fights the hardware graph, redesign may be better than post hoc acceptance.",
|
||
"Constraint-aware design starts before transpilation, not after it.",
|
||
],
|
||
canonical_story="begin from an abstract multi-qubit interaction pattern, then manually rethink the structure so the hardware graph becomes a design partner rather than a late-stage obstacle",
|
||
design_lens="Ask where the abstract circuit is over-demanding the topology and which manual rewrite might relieve that pressure.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(4, 4, name="hardware_studio")
|
||
# [1] prepare the logical source of correlation
|
||
circuit.h(0)
|
||
# [2] request a long-range interaction
|
||
circuit.cx(0, 3)
|
||
# [3] add a second interaction in the middle of the line
|
||
circuit.cx(1, 2)
|
||
# [4] tie the first source into the middle pair
|
||
circuit.cx(0, 2)
|
||
# [5] measure every wire for comparison
|
||
circuit.measure([0, 1, 2, 3], [0, 1, 2, 3])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare the logical source qubit", "diagram_effect": "Create the initial branching source", "why_it_matters": "This defines where later correlation originates."},
|
||
{"marker": "[2]", "code_focus": "Long-range interaction to qubit 3", "diagram_effect": "Force the most topology-stressed request in the circuit", "why_it_matters": "This is the pressure point to scrutinize first."},
|
||
{"marker": "[3]", "code_focus": "Mid-line interaction", "diagram_effect": "Add a relatively cheap local entangling step", "why_it_matters": "Not all interactions are equally expensive under the same graph."},
|
||
{"marker": "[4]", "code_focus": "Bridge from source into the middle pair", "diagram_effect": "Create a second topology-sensitive dependency", "why_it_matters": "This tests whether a manual rewrite might rearrange the logic more cleanly."},
|
||
{"marker": "[5]", "code_focus": "Measure all qubits", "diagram_effect": "Expose the current design empirically", "why_it_matters": "A redesign still needs observable consequences."},
|
||
],
|
||
analysis_code="""
|
||
from qiskit import QuantumCircuit
|
||
from qiskit.providers.basic_provider import BasicSimulator
|
||
from quantum_learning.experiments import line_coupling_map, transpile_summary
|
||
|
||
abstract = QuantumCircuit(4, name="hardware_studio")
|
||
abstract.h(0)
|
||
abstract.cx(0, 3)
|
||
abstract.cx(1, 2)
|
||
abstract.cx(0, 2)
|
||
|
||
summary = transpile_summary(
|
||
abstract,
|
||
BasicSimulator(),
|
||
basis_gates=["rz", "sx", "x", "cx"],
|
||
coupling_map=line_coupling_map(4),
|
||
)
|
||
|
||
{key: value for key, value in summary.items() if key != "compiled_circuit"}
|
||
""",
|
||
analysis_caption="The analysis cell makes the cost visible. Your job is to use that visibility to propose a better pre-transpilation design rather than letting the compiler carry all of the burden alone.",
|
||
common_mistakes=[
|
||
"assuming the transpiler’s first answer is automatically the design you should accept",
|
||
"complaining about layout pressure without proposing a structural rewrite",
|
||
"forgetting that logical dependencies can sometimes be reorganized before compilation",
|
||
],
|
||
heuristics=[
|
||
"Locate the graph-mismatched interactions before you start optimizing.",
|
||
"Compare what the circuit is logically asking for to what the topology can cheaply provide.",
|
||
"A manual rewrite is justified when it makes the constraints easier to satisfy without changing the intended behavior.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What new skill is emphasized in this notebook?", "options": ["Accepting transpiler output without question", "Constraint-aware manual redesign", "Avoiding all two-qubit gates forever"], "correct_index": 1, "explanation": "The notebook is about hardware-aware redesign, not passive acceptance."},
|
||
{"prompt": "Why is a long-range interaction the first thing to inspect?", "options": ["It often creates the largest routing burden", "It is always mathematically wrong", "It deletes all measurements"], "correct_index": 0, "explanation": "Long-range requests are frequent cost drivers under restricted graphs."},
|
||
{"prompt": "What does it mean to treat the compiler as a collaborator?", "options": ["You still think and redesign instead of outsourcing all judgment", "You ignore its output completely", "You never inspect the compiled circuit"], "correct_index": 0, "explanation": "Collaboration means using compiler feedback as design information."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is a manual rewrite?", "options": ["A human redesign of the abstract circuit to better fit constraints", "A classical measurement step", "A plotting adjustment"], "correct_index": 0, "explanation": "Manual rewrites are structural design interventions."},
|
||
{"prompt": "When is a manual rewrite justified?", "options": ["When it relieves constraint pressure without changing the intended behavior", "Whenever a notebook contains CX", "Only if the compiler crashes"], "correct_index": 0, "explanation": "The point is better implementation under the same logical goal."},
|
||
{"prompt": "What identity shift is this notebook trying to create?", "options": ["From compiler user to hardware-aware designer", "From designer to markdown reader", "From qubit user to classical-only programmer"], "correct_index": 0, "explanation": "This is explicitly a design-identity transition."},
|
||
],
|
||
mastery_gate="Take a topology-stressed circuit, identify the worst pressure points, propose a manual rewrite, and justify why the new design should compile more gracefully.",
|
||
stretch_prompts=[
|
||
"Write two competing rewrites for the same abstract circuit and benchmark them.",
|
||
"Change which qubit carries the initial superposition and see whether the topology pressure changes.",
|
||
"Explain in prose why one logical dependency pattern is more hardware-friendly than another.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="09_verification_and_noise_debugging.ipynb",
|
||
title="Verification and Noise Debugging",
|
||
learning_objective="separate design bugs from expected noisy degradation and learn to justify that diagnosis with explicit checks",
|
||
professional_relevance="Professional designers do not merely notice that a result is wrong. They diagnose *why* it is wrong: design flaw, compilation artifact, noise effect, or mistaken expectation. Verification language is what makes that diagnosis disciplined.",
|
||
key_vocabulary=[
|
||
("invariant", "a property that should remain true if the design is correct"),
|
||
("diagnosis", "the explanation of what failure mode is actually present"),
|
||
("signal versus distortion", "the intended pattern contrasted with the observed corruption"),
|
||
("debugging criterion", "the standard by which you judge whether a circuit is acceptable"),
|
||
],
|
||
mental_models=[
|
||
"Not every discrepancy is a bug, but every discrepancy deserves classification.",
|
||
"Verification begins with explicit expectations, not with surprised observation.",
|
||
"Noise and logical flaws can look similar until you articulate the expected invariants.",
|
||
],
|
||
canonical_story="compare an ideal pattern to a disturbed one and use explicit criteria to decide whether the difference is consistent with noise, design error, or both",
|
||
design_lens="Before changing the circuit, state what should still be true if the design is basically correct.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import build_demo_noise_model, simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(2, 2, name="verification_probe")
|
||
# [1] prepare the control superposition
|
||
circuit.h(0)
|
||
# [2] build the intended correlation
|
||
circuit.cx(0, 1)
|
||
# [3] measure for empirical comparison
|
||
circuit.measure([0, 1], [0, 1])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare the source of branching", "diagram_effect": "Create the ideal split in possibilities", "why_it_matters": "This is part of the invariant story you later compare."},
|
||
{"marker": "[2]", "code_focus": "Correlate the second qubit", "diagram_effect": "Create the intended support pattern", "why_it_matters": "This determines which outcomes should dominate ideally."},
|
||
{"marker": "[3]", "code_focus": "Measure both qubits", "diagram_effect": "Expose empirical behavior for verification", "why_it_matters": "This is where you compare expectation to observation."},
|
||
],
|
||
analysis_code="""
|
||
from quantum_learning import bell_circuit, build_demo_noise_model, counts_to_probabilities, simulate_counts
|
||
|
||
ideal = counts_to_probabilities(simulate_counts(bell_circuit(), shots=1024, seed=11))
|
||
noisy = counts_to_probabilities(
|
||
simulate_counts(
|
||
bell_circuit(),
|
||
shots=1024,
|
||
seed=11,
|
||
noise_model=build_demo_noise_model(readout_error=0.08),
|
||
)
|
||
)
|
||
|
||
all_keys = sorted(set(ideal) | set(noisy))
|
||
l1_distance = sum(abs(ideal.get(key, 0.0) - noisy.get(key, 0.0)) for key in all_keys)
|
||
|
||
ideal, noisy, l1_distance
|
||
""",
|
||
analysis_caption="This cell gives you a concrete distortion measure and the underlying distributions. The job is to move from seeing a difference to classifying that difference with discipline.",
|
||
common_mistakes=[
|
||
"calling every deviation a bug without checking whether the ideal invariant mostly survives",
|
||
"blaming noise for a genuinely incorrect logical design",
|
||
"changing the circuit before writing down what a correct circuit should still preserve",
|
||
],
|
||
heuristics=[
|
||
"State the invariant first, then inspect the observed failure.",
|
||
"Ask whether the wrong outcomes are qualitatively new or merely inflated by disturbance.",
|
||
"A diagnosis is stronger when it names the criterion that led to it.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is an invariant in this context?", "options": ["A property that should remain true if the design is correct", "A type of backend", "A plotting option"], "correct_index": 0, "explanation": "Invariants are the expectations you verify against."},
|
||
{"prompt": "Why write the expectation before changing the circuit?", "options": ["To make the diagnosis disciplined instead of reactive", "To slow down the notebook for no reason", "To remove all noise"], "correct_index": 0, "explanation": "Verification depends on explicit criteria."},
|
||
{"prompt": "What is one risk in noisy debugging?", "options": ["Confusing expected distortion with a logical bug", "Using too many markdown cells", "Having classical bits"], "correct_index": 0, "explanation": "Noise and design bugs can be confused if criteria are weak."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What is the central habit this notebook is training?", "options": ["Reactive tinkering", "Disciplined classification of discrepancies", "Avoidance of all interpretation"], "correct_index": 1, "explanation": "The notebook is about structured verification and diagnosis."},
|
||
{"prompt": "What makes a diagnosis strong?", "options": ["It names the criterion behind the judgment", "It uses very dramatic language", "It avoids numbers entirely"], "correct_index": 0, "explanation": "Criteria make debugging arguments defensible."},
|
||
{"prompt": "What should happen before you call a circuit incorrect?", "options": ["You should state the expected invariant and compare observed failure to it", "You should delete the notebook", "You should trust the first surprising output"], "correct_index": 0, "explanation": "Verification begins with explicit expectations."},
|
||
],
|
||
mastery_gate="Given ideal and distorted behavior, write an explicit invariant, diagnose whether the discrepancy is due to noise, design error, or both, and justify the conclusion with a concrete criterion.",
|
||
stretch_prompts=[
|
||
"Deliberately break one gate and compare that failure pattern to a noisy-but-correct pattern.",
|
||
"Write two different diagnostic criteria and see whether they agree.",
|
||
"Summarize a debugging decision as if you were writing a design review memo.",
|
||
],
|
||
),
|
||
TechnicalSpec(
|
||
filename="10_capstone_circuit_design_review.ipynb",
|
||
title="Capstone Circuit Design Review",
|
||
learning_objective="treat circuit design as a professional review process involving alternatives, metrics, risks, and a defended final recommendation",
|
||
professional_relevance="This notebook is the closest thing in the course to real engineering practice. A professional designer does not present a single circuit as destiny. They compare candidate designs, benchmark them, discuss risks, and justify a recommendation.",
|
||
key_vocabulary=[
|
||
("candidate design", "one proposed circuit family or implementation choice"),
|
||
("review memo", "a structured written justification of a design decision"),
|
||
("risk", "a plausible failure mode or engineering concern tied to a design choice"),
|
||
("recommendation", "the defended final choice after comparing alternatives"),
|
||
],
|
||
mental_models=[
|
||
"A good final answer is usually comparative, not absolute.",
|
||
"Metrics matter, but they do not eliminate judgment.",
|
||
"A review is strongest when it makes tradeoffs explicit instead of hiding them.",
|
||
],
|
||
canonical_story="start from a design problem, create at least two candidate circuit approaches, compare them under explicit metrics, then defend the final recommendation as if writing to another engineer",
|
||
design_lens="Ask what decision you are making, what evidence supports it, and what risks remain even after you decide.",
|
||
editable_imports="""
|
||
from qiskit import QuantumCircuit
|
||
|
||
from quantum_learning.experiments import simulate_counts
|
||
""",
|
||
editable_context_names=["QuantumCircuit", "simulate_counts"],
|
||
editable_code="""
|
||
circuit = QuantumCircuit(3, 3, name="candidate_a")
|
||
# [1] prepare the logical source qubit
|
||
circuit.h(0)
|
||
# [2] spread correlation to qubit 1
|
||
circuit.cx(0, 1)
|
||
# [3] extend the pattern to qubit 2
|
||
circuit.cx(1, 2)
|
||
# [4] measure for candidate comparison
|
||
circuit.measure([0, 1, 2], [0, 1, 2])
|
||
""",
|
||
step_refs=[
|
||
{"marker": "[1]", "code_focus": "Prepare the source qubit", "diagram_effect": "Create the initial resource for the candidate design", "why_it_matters": "Review begins by naming what the candidate is trying to achieve."},
|
||
{"marker": "[2]", "code_focus": "First extension step", "diagram_effect": "Spread the candidate pattern to a second qubit", "why_it_matters": "This reveals part of the candidate architecture."},
|
||
{"marker": "[3]", "code_focus": "Second extension step", "diagram_effect": "Complete the three-qubit candidate pattern", "why_it_matters": "The full candidate can now be compared to alternatives."},
|
||
{"marker": "[4]", "code_focus": "Measurement layer", "diagram_effect": "Expose candidate behavior for benchmarking", "why_it_matters": "Review requires observable evidence, not just intention."},
|
||
],
|
||
analysis_code="""
|
||
project_choice = ""
|
||
candidate_a = ""
|
||
candidate_b = ""
|
||
decision_metric_table = []
|
||
|
||
{
|
||
"project_choice": project_choice,
|
||
"candidate_a": candidate_a,
|
||
"candidate_b": candidate_b,
|
||
"decision_metric_table": decision_metric_table,
|
||
}
|
||
""",
|
||
analysis_caption="This cell is intentionally open-ended. It is the template for your own review memo. The notebook stops being a lecture here and becomes a studio for defended decision-making.",
|
||
common_mistakes=[
|
||
"presenting one candidate as if alternatives never existed",
|
||
"using metrics without saying why those metrics matter for the task",
|
||
"hiding risks because they make the final recommendation look less clean",
|
||
],
|
||
heuristics=[
|
||
"Always compare at least two plausible designs before making a recommendation.",
|
||
"State which metrics you care about and why they matter for this problem.",
|
||
"A professional recommendation includes residual risk, not just confidence.",
|
||
],
|
||
quiz_a=[
|
||
{"prompt": "What is the central shift in the capstone notebook?", "options": ["From isolated circuits to defended design decisions", "From quantum to classical-only programming", "From notebooks to no writing at all"], "correct_index": 0, "explanation": "The capstone is about professional review and recommendation."},
|
||
{"prompt": "Why compare multiple candidates?", "options": ["Because the first idea is rarely above review", "Because metrics are illegal otherwise", "Because all circuits are equivalent"], "correct_index": 0, "explanation": "Comparison is core to real design judgment."},
|
||
{"prompt": "What should a final recommendation include?", "options": ["Only confidence and no caveats", "Tradeoffs, evidence, and residual risks", "A list of gate names"], "correct_index": 1, "explanation": "Professional decisions remain accountable to evidence and risk."},
|
||
],
|
||
quiz_b=[
|
||
{"prompt": "What weakens a design review?", "options": ["Hiding alternatives and pretending one design was inevitable", "Explaining tradeoffs explicitly", "Writing a recommendation"], "correct_index": 0, "explanation": "Good reviews make the decision space visible."},
|
||
{"prompt": "Why are metrics not enough by themselves?", "options": ["Because judgment is still needed to interpret which metrics matter most", "Because metrics cannot be written down", "Because metrics only apply to single-qubit circuits"], "correct_index": 0, "explanation": "Metrics inform judgment; they do not replace it."},
|
||
{"prompt": "What identity does the capstone aim to confirm?", "options": ["Independent circuit designer who can defend decisions", "Passive notebook executor", "Purely theoretical spectator"], "correct_index": 0, "explanation": "The capstone is meant to validate professional design identity."},
|
||
],
|
||
mastery_gate="Frame a circuit problem, create at least two plausible candidates, benchmark them under explicit criteria, and write a final recommendation that names both strengths and remaining risks.",
|
||
stretch_prompts=[
|
||
"Choose a problem from an earlier notebook and rewrite it as a full review memo with competing candidates.",
|
||
"Add a hardware-aware criterion to your decision table and see whether the recommendation changes.",
|
||
"Write the final recommendation in the tone of a real engineering review document.",
|
||
],
|
||
),
|
||
]
|
||
|
||
|
||
def build_readme_notebook_redirect() -> None:
|
||
pass
|
||
|
||
|
||
def build_all() -> None:
|
||
NOTEBOOKS_DIR.mkdir(parents=True, exist_ok=True)
|
||
|
||
outputs: dict[Path, dict] = {
|
||
NOTEBOOKS_DIR / "START_HERE.ipynb": build_start_here(),
|
||
}
|
||
|
||
for spec in TECHNICAL_SPECS:
|
||
outputs[NOTEBOOKS_DIR / ".legacy_single_notebook_path" / spec.filename] = (
|
||
build_technical_notebook(spec)
|
||
)
|
||
|
||
for output_path, payload in outputs.items():
|
||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||
with output_path.open("w", encoding="utf-8") as handle:
|
||
json.dump(payload, handle, indent=1)
|
||
handle.write("\n")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
build_all()
|