mirror of
https://github.com/saymrwulf/QuantumLearning.git
synced 2026-05-14 20:58:00 +00:00
305 lines
18 KiB
Text
305 lines
18 KiB
Text
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Grover and Amplitude Amplification Lecture\n"
|
|
],
|
|
"id": "f49a9352"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Grover is the last algorithmic-design module in the band because it forces several earlier ideas to operate together at once. You need oracle thinking from the Deutsch family, family-level builder discipline from Bernstein-Vazirani, and structured reasoning about repeated transformations that is compatible with the QFT's emphasis on mechanism. Grover is often taught as a famous speedup. Here it is taught as a composed design: build a phase-marking oracle, build a diffuser that acts as a reflection about the mean, decide how many times to apply the pair, and defend the resulting circuit under review.\n"
|
|
],
|
|
"id": "28abf173"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Learning Objective\n",
|
|
"\n",
|
|
"\n",
|
|
" By the end of this lecture you should be able to explain the distinct roles of the oracle and diffuser, describe amplitude amplification as a controlled rotation rather than vague strengthening, choose an iteration count for a tiny search space, and critique a Grover notebook that hides target-marking logic behind unreadable helper code.\n"
|
|
],
|
|
"id": "0d9862d9"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The first mistake this module tries to prevent is treating Grover as a one-word explanation. Saying that Grover performs search is true but insufficient. Search is the task. The circuit mechanism is more specific: one block marks a target by phase, another reflects amplitudes about their mean, and repeating the pair rotates the state toward the marked candidate. That sentence is already much more useful than the word search alone because it names the actual design burdens. The rest of the notebook makes those burdens explicit in code.\n"
|
|
],
|
|
"id": "bfc20d5a"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Separating the oracle and diffuser is pedagogically essential. If you collapse them into one opaque helper, the routine will still run but the student loses the ability to inspect which block chooses the target and which block performs amplification. This course treats that loss as serious. When a later capstone asks you to defend a design, you will need to say whether a problem came from target-marking logic, amplification logic, or iteration choice. Grover is the module where that separation becomes normal.\n"
|
|
],
|
|
"id": "4de2f3cc"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The geometric language of rotation is not decorative either. It explains why one more iteration is not automatically better. In a small search space, one iteration may already place most of the amplitude on the target. A second iteration can overshoot. That is why the module stays intentionally small. On two qubits, the effect is easy to see, easy to count, and easy to connect back to design judgment. Small systems are where mechanism becomes visible rather than drowned by scale.\n"
|
|
],
|
|
"id": "9c77bd99"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Oracle design remains central here. The target is implemented through phase marking, often with X wrappers around a central controlled-phase structure. The details are local but meaningful. Change the wrappers and you change which state is marked. Write the wrappers unclearly and you make the circuit hard to review even if the counts happen to peak on the right outcome. This is exactly the kind of difference between execution and engineering that the whole course is trying to teach.\n"
|
|
],
|
|
"id": "27959873"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"The diffuser then deserves its own explanation. It is not just a block that comes after the oracle because the textbook says so. It is the block that reflects amplitudes about their average and thereby turns a phase-marked distinction into a measurement advantage. If that sentence feels too abstract, the lab will make it concrete. But the lecture still has to say it plainly, because without that meaning the diffuser becomes another memorized shape with no transfer value.\n"
|
|
],
|
|
"id": "060d244a"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Grover therefore closes the algorithmic-design band on the right note. It is still small enough to study locally, yet rich enough to require oracle reasoning, helper discipline, geometric explanation, and explicit design tradeoffs. By the time you leave the bundle, the phrase algorithmic design should feel much less like an aspiration and much more like a practiced habit.\n"
|
|
],
|
|
"id": "3a4f9ecc"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from pathlib import Path\n",
|
|
"import sys\n",
|
|
"\n",
|
|
"project_root = Path.cwd().resolve()\n",
|
|
"while not (project_root / \"pyproject.toml\").exists():\n",
|
|
" if project_root.parent == project_root:\n",
|
|
" raise RuntimeError(\"Could not locate the project root from this notebook.\")\n",
|
|
" project_root = project_root.parent\n",
|
|
"\n",
|
|
"src_path = project_root / \"src\"\n",
|
|
"if str(src_path) not in sys.path:\n",
|
|
" sys.path.insert(0, str(src_path))\n"
|
|
],
|
|
"id": "d4700ff6"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from math import pi\n",
|
|
"\n",
|
|
"from quantum_learning import (\n",
|
|
" counts_to_probabilities,\n",
|
|
" draw_circuit,\n",
|
|
" editable_circuit_lab,\n",
|
|
" plot_counts,\n",
|
|
" plot_probabilities,\n",
|
|
" quiz_block,\n",
|
|
" reflection_box,\n",
|
|
" simulate_counts,\n",
|
|
" statevector_probabilities,\n",
|
|
" step_reference_table,\n",
|
|
")\n",
|
|
"from qiskit import QuantumCircuit\n",
|
|
"from qiskit.quantum_info import Statevector\n"
|
|
],
|
|
"id": "4c0cfb48"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Code-To-Diagram Anchor\n",
|
|
"\n",
|
|
"\n",
|
|
" The anchor circuit below uses a two-qubit search space so that target marking, diffusion, and iteration choice all remain visible. Read the reference table first, then edit the target and iteration choices carefully enough that you can still explain the mechanism in plain language.\n"
|
|
],
|
|
"id": "f612e1e0"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"step_reference_table([{'marker': '[1]', 'code_focus': 'Prepare the search register in a uniform superposition.', 'diagram_effect': 'The circuit opens with a clean equal-weight state over all candidates.', 'why_it_matters': 'Amplitude amplification starts from a fair baseline, not from a pre-biased guess.'}, {'marker': '[2]', 'code_focus': 'Implement the oracle as a phase-marking operation on the chosen target.', 'diagram_effect': 'The middle block identifies the marked state without directly measuring it.', 'why_it_matters': 'Grover oracles mark by phase, not by classical tagging.'}, {'marker': '[3]', 'code_focus': 'Apply the diffuser as a reflection about the mean amplitude.', 'diagram_effect': 'The circuit mirrors the oracle block with a deliberate inversion pattern.', 'why_it_matters': 'The diffuser is the engineering heart of amplitude amplification.'}, {'marker': '[4]', 'code_focus': 'Choose the iteration count as a design decision, then measure.', 'diagram_effect': 'The right edge of the circuit closes the amplification loop with a concrete reporting contract.', 'why_it_matters': 'Over-iteration is a real design failure, not a harmless extra pass.'}])\n"
|
|
],
|
|
"id": "07c6bb38"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"editable_code = '\\nfrom qiskit import QuantumCircuit\\n\\ndef grover_oracle(target: str = \"11\") -> QuantumCircuit:\\n oracle = QuantumCircuit(2, name=f\"oracle_{target}\")\\n if target[0] == \"0\":\\n oracle.x(1)\\n if target[1] == \"0\":\\n oracle.x(0)\\n oracle.cz(0, 1)\\n if target[1] == \"0\":\\n oracle.x(0)\\n if target[0] == \"0\":\\n oracle.x(1)\\n return oracle\\n\\ndef diffuser() -> QuantumCircuit:\\n circuit = QuantumCircuit(2, name=\"diffuser\")\\n circuit.h([0, 1])\\n circuit.x([0, 1])\\n circuit.cz(0, 1)\\n circuit.x([0, 1])\\n circuit.h([0, 1])\\n return circuit\\n\\ncircuit = QuantumCircuit(2, 2)\\n# [1] Uniform search state\\ncircuit.h([0, 1])\\n# [2] Mark the target by phase\\ncircuit.compose(grover_oracle(\"11\"), inplace=True)\\n# [3] Reflect about the mean\\ncircuit.compose(diffuser(), inplace=True)\\n# [4] Report the amplified candidate\\ncircuit.measure([0, 1], [0, 1])\\n'\n",
|
|
"editable_circuit_lab(\n",
|
|
" initial_code=editable_code,\n",
|
|
" context={\"QuantumCircuit\": QuantumCircuit, \"simulate_counts\": simulate_counts},\n",
|
|
" title='Grover and Amplitude Amplification Anchor',\n",
|
|
" instructions='Edit one causal region at a time. Use the reference table to keep code changes tied to circuit meaning.',\n",
|
|
" shots=256,\n",
|
|
")\n"
|
|
],
|
|
"id": "c6b486fb"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def grover_run(target: str, iterations: int) -> dict[str, object]:\n",
|
|
" def grover_oracle(target: str) -> QuantumCircuit:\n",
|
|
" oracle = QuantumCircuit(2, name=f\"oracle_{target}\")\n",
|
|
" if target[0] == \"0\":\n",
|
|
" oracle.x(1)\n",
|
|
" if target[1] == \"0\":\n",
|
|
" oracle.x(0)\n",
|
|
" oracle.cz(0, 1)\n",
|
|
" if target[1] == \"0\":\n",
|
|
" oracle.x(0)\n",
|
|
" if target[0] == \"0\":\n",
|
|
" oracle.x(1)\n",
|
|
" return oracle\n",
|
|
"\n",
|
|
" def diffuser() -> QuantumCircuit:\n",
|
|
" circuit = QuantumCircuit(2, name=\"diffuser\")\n",
|
|
" circuit.h([0, 1])\n",
|
|
" circuit.x([0, 1])\n",
|
|
" circuit.cz(0, 1)\n",
|
|
" circuit.x([0, 1])\n",
|
|
" circuit.h([0, 1])\n",
|
|
" return circuit\n",
|
|
"\n",
|
|
" circuit = QuantumCircuit(2, 2)\n",
|
|
" circuit.h([0, 1])\n",
|
|
" for _ in range(iterations):\n",
|
|
" circuit.compose(grover_oracle(target), inplace=True)\n",
|
|
" circuit.compose(diffuser(), inplace=True)\n",
|
|
" circuit.measure([0, 1], [0, 1])\n",
|
|
" return {\"target\": target, \"iterations\": iterations, \"counts\": simulate_counts(circuit, shots=256)}\n",
|
|
"\n",
|
|
"[grover_run(\"11\", iterations) for iterations in [0, 1, 2]]\n"
|
|
],
|
|
"id": "c65c2707"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'What does a Grover oracle do in the small-search setting?', 'options': ['It marks the target state by phase', 'It measures the winning answer directly', 'It replaces the diffuser'], 'correct_index': 0, 'explanation': 'The oracle marks, the diffuser amplifies.'}, {'prompt': 'What is the diffuser best understood as?', 'options': ['A reflection about the mean amplitude', 'A generic entangling block with no geometric meaning', 'A noise-mitigation trick'], 'correct_index': 0, 'explanation': 'Grover becomes teachable once the diffuser has geometric meaning.'}, {'prompt': 'Why is iteration count part of the design story?', 'options': ['Because too many iterations rotate past the target instead of improving forever', 'Because each iteration changes the number of qubits', 'Because the oracle stops working after one pass'], 'correct_index': 0, 'explanation': 'Amplitude amplification is a controlled rotation, not monotone accumulation.'}], heading='Lecture Checkpoint A')\n"
|
|
],
|
|
"id": "fe876c68"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"A strong self-check in this module is whether you can explain a bad result without panicking. If a second iteration weakens the target peak, that is not bizarre behavior. It is exactly what the geometric story predicts in a small search space. Modules become professional only when surprising outputs can be narrated rather than merely observed.\n"
|
|
],
|
|
"id": "f5a1bd74"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Another strong self-check is whether your oracle helper remains readable across target changes. If changing the target string feels like spelunking through index tricks, the builder is not yet carrying its design burden well enough. Grover makes that weakness visible very quickly, which is one reason it is such a good teaching module.\n"
|
|
],
|
|
"id": "b63acfd4"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Reading Discipline For This Module\n",
|
|
"\n",
|
|
"A serious lecture notebook has to teach more than recognition. It has to teach what to look at, what to ask, and what kind of evidence would count as a meaningful check. That is why this module keeps repeating a small number of disciplined questions. Which region prepares the state or question format? Which region encodes the task-specific structure? Which region translates that hidden structure into evidence you can actually read? And which parts of the notebook are presentation choices rather than mechanism? Those questions may sound repetitive, but repetition is useful here because algorithmic circuits become opaque very quickly when the learner loses the habit of dividing them into roles.\n",
|
|
"\n",
|
|
"Another reason for this slower lecture style is that professional design does not tolerate admiration as a substitute for analysis. It is perfectly possible to feel impressed by an algorithm, reproduce the overall diagram, and still be unable to explain what would break if one line changed. This course is trying to build the opposite habit. A mature notebook reader should be able to look at a circuit region and say what burden it carries, what evidence would test it, and what kind of mistake would falsify the current explanation. If that standard feels demanding, that is appropriate. The goal is to create designers, not spectators.\n"
|
|
],
|
|
"id": "7a5f3223"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Forward Link\n",
|
|
"\n",
|
|
"One reason these notebooks are written so densely is that later professional-design modules will assume this vocabulary is stable. When you later compare transpiled candidates, argue about noise sensitivity, or defend a capstone recommendation, you will not have time to rediscover what an oracle contract, reporting convention, controlled phase, or iteration choice means. The language has to be ready. That is why this lecture insists on precision now. The details are not there to slow you down forever. They are there so later speed is built on something trustworthy.\n"
|
|
],
|
|
"id": "9bf07c99"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"quiz_block([{'prompt': 'What is the most important beginner-professional lesson in Grover?', 'options': ['How oracle marking and diffusion compose into a tunable amplification routine', 'How to avoid thinking geometrically', 'How to replace search with measurement'], 'correct_index': 0, 'explanation': 'The module is about composing two reflections into a controlled search routine.'}, {'prompt': 'Why should the oracle and diffuser often be implemented as separate helpers?', 'options': ['Because they play different roles and must be inspected independently during review', 'Because Qiskit forbids inline composition', 'Because the diffuser is always backend-specific'], 'correct_index': 0, 'explanation': 'Separation improves auditability and experimentation.'}, {'prompt': 'What would be a weak explanation of a good Grover result?', 'options': ['The probabilities went up because quantum is strong', 'One iteration reflected the marked amplitude and then the mean-reflection boosted it', 'The oracle marked a state and the diffuser redistributed amplitudes around that mark'], 'correct_index': 0, 'explanation': 'The course is trying to eliminate hand-waving explanations like this.'}], heading='Lecture Checkpoint B')\n"
|
|
],
|
|
"id": "c4827246"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Explain the difference between oracle marking and diffusion in Grover without collapsing them into one vague sentence.')\n"
|
|
],
|
|
"id": "0a8a2d2d"
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"reflection_box('Why is iteration count an engineering choice rather than a fixed ritual in this module?')\n"
|
|
],
|
|
"id": "c9356914"
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Mastery Gate\n",
|
|
"\n",
|
|
"Leave this lecture only when you can explain the mechanism line by line, not just recognize the diagram.\n"
|
|
],
|
|
"id": "f3b127e3"
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "QuantumLearning (.venv)",
|
|
"language": "python",
|
|
"name": "quantum-learning"
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.12"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|