autoresearch-quantum/notebooks/plan_d/experiment_1_protection.ipynb
saymrwulf 18f5bef127 Add foolproof course navigation: central entry point and inter-notebook links
- Create notebooks/00_START_HERE.ipynb as the single entry point with plan
  descriptions, audience guidance, and links to all 4 plans
- Add navigation footer cells to all 11 content notebooks with Next/Previous
  links and back-link to Start Here
- Terminal notebooks (plan endings) offer cross-plan links to explore other plans
- Plan C dashboard gets explicit recommended reading order (Track A → B → C)
- Add test_start_here_exists_and_links_all_plans and
  test_every_notebook_has_navigation_footer to test suite
- Skip navigation-only notebooks in code-cell and assessment tests
2026-04-15 19:25:39 +02:00

567 lines
No EOL
20 KiB
Text

{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipywidgets)",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.14.0"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Experiment 1: Can Quantum Error Detection Protect a Magic State?\n",
"\n",
"---\n",
"\n",
"## Hypothesis\n",
"\n",
"> **H1:** The $[\\![4,2,2]\\!]$ quantum error-detecting code can encode a\n",
"> single-qubit magic state $|T\\rangle$ such that (a) the magic-state\n",
"> character is fully preserved, and (b) every single-qubit error is\n",
"> detectable by stabiliser measurement.\n",
"\n",
"### Why this matters\n",
"\n",
"Fault-tolerant quantum computing needs the $T$-gate, but the $T$-gate\n",
"cannot be implemented transversally on most error-correcting codes\n",
"(Eastin\u2013Knill theorem). The workaround is to prepare a **magic state**\n",
"$|T\\rangle = (|0\\rangle + e^{i\\pi/4}|1\\rangle)/\\sqrt{2}$ and consume\n",
"it via gate teleportation.\n",
"\n",
"But a bare qubit has no error protection. If noise corrupts $|T\\rangle$\n",
"before we use it, the entire computation is silently wrong. We need to\n",
"**encode** $|T\\rangle$ into an error-detecting code so that corrupted\n",
"copies can be identified and discarded.\n",
"\n",
"**The question:** Does the encoding actually work? Does it preserve the\n",
"magic, and can it catch errors?\n",
"\n",
"### Claim\n",
"\n",
"We claim that after encoding into the $[\\![4,2,2]\\!]$ code:\n",
"1. The magic witness $W = 1.0$ (perfect magic preserved).\n",
"2. Both stabiliser expectations are $+1$ (valid codeword).\n",
"3. Every single-qubit Pauli error ($X$, $Z$, $Y$) flips at least one\n",
" stabiliser from $+1$ to $-1$.\n",
"4. Postselection on syndrome \"00\" correctly filters all detected errors."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"%matplotlib inline\n",
"import warnings; warnings.filterwarnings(\"ignore\")\n",
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from math import pi, sqrt\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import Statevector, SparsePauliOp, state_fidelity\n",
"from qiskit.visualization import plot_bloch_multivector\n",
"from qiskit_aer import AerSimulator\n",
"\n",
"from autoresearch_quantum.codes.four_two_two import (\n",
" build_preparation_circuit, build_encoder, apply_magic_seed,\n",
" encoded_magic_statevector, STABILIZERS, MEASUREMENT_OPERATORS, DATA_QUBITS,\n",
")\n",
"from autoresearch_quantum.experiments.encoded_magic_state import build_circuit_bundle\n",
"from autoresearch_quantum.models import ExperimentSpec\n",
"from autoresearch_quantum.execution.analysis import logical_magic_witness\n",
"\n",
"print(\"All imports successful.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {},
"source": [
"from autoresearch_quantum.teaching import LearningTracker\n",
"from autoresearch_quantum.teaching.assess import quiz, predict_choice, reflect, order, checkpoint_summary\n",
"tracker = LearningTracker(\"plan_d_exp1\")\n",
"print(\"Learning tracker active.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 1: The Magic State on a Single Qubit\n",
"\n",
"Before we can test the encoding, we need to understand what we're\n",
"encoding. The magic state is:\n",
"\n",
"$$|T\\rangle = \\frac{|0\\rangle + e^{i\\pi/4}|1\\rangle}{\\sqrt{2}}$$\n",
"\n",
"It lives on the **equator** of the Bloch sphere, at $45\u00b0$ between the\n",
"$+X$ and $+Y$ axes. Its special property: it enables the $T$-gate via\n",
"gate teleportation \u2014 the key non-Clifford resource for universal quantum\n",
"computing."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Build the T-state\n",
"qc = QuantumCircuit(1, name=\"|T>\")\n",
"qc.h(0)\n",
"qc.p(pi/4, 0)\n",
"\n",
"t_state = Statevector.from_instruction(qc)\n",
"print(\"T-state amplitudes:\")\n",
"print(f\" |0>: {t_state[0]:.4f}\")\n",
"print(f\" |1>: {t_state[1]:.4f}\")\n",
"print(f\" |1> phase: {np.angle(t_state[1])*180/pi:.1f} degrees = pi/4\")\n",
"\n",
"# Bloch coordinates\n",
"bloch = [t_state.expectation_value(SparsePauliOp(p)).real for p in ['X', 'Y', 'Z']]\n",
"print(f\"\\nBloch coordinates:\")\n",
"print(f\" <X> = {bloch[0]:.4f} (expected: 1/sqrt(2) = {1/sqrt(2):.4f})\")\n",
"print(f\" <Y> = {bloch[1]:.4f} (expected: 1/sqrt(2) = {1/sqrt(2):.4f})\")\n",
"print(f\" <Z> = {bloch[2]:.4f} (on the equator)\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {},
"source": [
"quiz(tracker, \"q1_tstate_phase\",\n",
" question=\"What is the phase of the |1\\u27E9 coefficient in the T-state?\",\n",
" options=[\"\\u03C0/2 (90\\u00b0)\", \"\\u03C0/4 (45\\u00b0)\", \"\\u03C0/8 (22.5\\u00b0)\"],\n",
" correct=1, section=\"1. T-state\", bloom=\"remember\",\n",
" explanation=\"\\u03C0/4 = 45\\u00b0. The gate is called T (\\u03C0/8 on the Bloch sphere), but the state phase is \\u03C0/4.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 2: Encoding into the $[\\![4,2,2]\\!]$ Code\n",
"\n",
"The $[\\![4,2,2]\\!]$ code uses **4 physical qubits** to encode **2 logical\n",
"qubits** with **distance 2** (detects any single-qubit error).\n",
"\n",
"- **Logical qubit 0** (\"the magic qubit\"): will hold $|T\\rangle$.\n",
"- **Logical qubit 1** (\"the spectator\"): stays in $|0\\rangle_L$.\n",
"\n",
"The codespace is the simultaneous $+1$ eigenspace of two stabilisers:\n",
"- $S_X = XXXX$\n",
"- $S_Z = ZZZZ$\n",
"\n",
"Any state inside the codespace satisfies $\\langle XXXX \\rangle = +1$\n",
"and $\\langle ZZZZ \\rangle = +1$. An error kicks the state out of the\n",
"codespace, flipping at least one eigenvalue to $-1$."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Build the full preparation: seed (H+P) on qubit 0, then encode all 4\n",
"prep = build_preparation_circuit(\"h_p\", \"cx_chain\")\n",
"print(f\"Preparation circuit: {prep.num_qubits} qubits, depth {prep.depth()}\")\n",
"prep.draw(\"mpl\", style=\"iqp\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Compute the encoded statevector\n",
"state = encoded_magic_statevector()\n",
"print(f\"Statevector has {len(state)} amplitudes (2^4 = 16)\")\n",
"print(f\"\\nNon-zero amplitudes (the codespace):\")\n",
"for i, amp in enumerate(state.data):\n",
" if abs(amp) > 1e-10:\n",
" print(f\" |{i:04b}> : {amp:.4f} (magnitude: {abs(amp):.4f})\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {},
"source": [
"predict_choice(tracker, \"q2_nonzero\",\n",
" question=\"How many of the 16 basis states have non-zero amplitude?\",\n",
" options=[\"2\", \"4\", \"8\", \"All 16\"],\n",
" correct=1, section=\"2. Encoding\", bloom=\"understand\",\n",
" explanation=\"Only 4 basis states (0000, 0101, 1010, 1111) have non-zero amplitude. These span the codespace of the [[4,2,2]] code.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 3: Testing Claim (2) \u2014 Stabiliser Verification\n",
"\n",
"**Claim:** Both stabiliser expectations are $+1$, confirming the\n",
"encoded state is a valid codeword."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Verify stabiliser expectations\n",
"state = encoded_magic_statevector()\n",
"for name, stab in STABILIZERS.items():\n",
" exp = state.expectation_value(stab).real\n",
" status = \"PASS\" if abs(exp - 1.0) < 1e-6 else \"FAIL\"\n",
" print(f\" <{name}> = {exp:+.6f} [{status}]\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Result:** Both stabilisers read $+1$. The state is in the codespace. \\checkmark"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"quiz(tracker, \"q3_stabilizer_meaning\",\n",
" question=\"\\u27E8ZZZZ\\u27E9 = +1 tells us:\",\n",
" options=[\n",
" \"All four qubits are in |0\\u27E9\",\n",
" \"The state is in the codespace \\u2014 no X-type error detected\",\n",
" \"The Z-gate has been applied to all qubits\",\n",
" ],\n",
" correct=1, section=\"3. Stabilisers\", bloom=\"understand\",\n",
" explanation=\"ZZZZ detects X errors (X anti-commutes with Z). Eigenvalue +1 means no X error is present.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 4: Testing Claim (3) \u2014 Every Single-Qubit Error Is Detectable\n",
"\n",
"**Claim:** Every single-qubit Pauli error ($X$, $Z$, $Y$ on any of the\n",
"4 qubits) flips at least one stabiliser from $+1$ to $-1$.\n",
"\n",
"We will systematically inject every possible single-qubit error and\n",
"check the stabilisers."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Complete error detection table\n",
"from qiskit.quantum_info import Operator\n",
"state = encoded_magic_statevector()\n",
"\n",
"errors_detected = 0\n",
"errors_total = 0\n",
"\n",
"header = f\"{'Error':14s} {'<XXXX>':>8s} {'<ZZZZ>':>8s} {'Detected by':>15s}\"\n",
"print(header)\n",
"print(\"=\" * len(header))\n",
"\n",
"for error_type in ['X', 'Y', 'Z']:\n",
" for qubit in range(4):\n",
" # Apply single-qubit error\n",
" error_gate = {'X': np.array([[0,1],[1,0]]),\n",
" 'Y': np.array([[0,-1j],[1j,0]]),\n",
" 'Z': np.array([[1,0],[0,-1]])}[error_type]\n",
" full_error = np.eye(1)\n",
" for q in range(4):\n",
" full_error = np.kron(full_error, error_gate if q == qubit else np.eye(2))\n",
" corrupted = Statevector(full_error @ state.data)\n",
"\n",
" xxxx = corrupted.expectation_value(STABILIZERS[\"x_stabilizer\"]).real\n",
" zzzz = corrupted.expectation_value(STABILIZERS[\"z_stabilizer\"]).real\n",
"\n",
" detected_by = []\n",
" if abs(xxxx - (-1)) < 0.01: detected_by.append(\"XXXX\")\n",
" if abs(zzzz - (-1)) < 0.01: detected_by.append(\"ZZZZ\")\n",
"\n",
" errors_total += 1\n",
" if detected_by:\n",
" errors_detected += 1\n",
"\n",
" det_str = \", \".join(detected_by) if detected_by else \"NONE!\"\n",
" print(f\"{error_type}(q{qubit}): {xxxx:+.1f} {zzzz:+.1f} {det_str}\")\n",
"\n",
"print(f\"\\nDetected: {errors_detected}/{errors_total} single-qubit errors\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Result:** All 12 single-qubit errors detected (12/12). \\checkmark\n",
"\n",
"- $X$ errors: detected by $ZZZZ$ (because $X$ anti-commutes with $Z$)\n",
"- $Z$ errors: detected by $XXXX$ (because $Z$ anti-commutes with $X$)\n",
"- $Y$ errors: detected by **both** (because $Y = iXZ$)"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"quiz(tracker, \"q4_which_detects\",\n",
" question=\"A Z error on qubit 2 occurs. Which stabiliser detects it?\",\n",
" options=[\n",
" \"ZZZZ (because Z commutes with Z \\u2014 wait, that means it does NOT detect it)\",\n",
" \"XXXX (because Z anti-commutes with X, flipping the eigenvalue)\",\n",
" \"Neither \\u2014 Z errors are invisible\",\n",
" ],\n",
" correct=1, section=\"4. Error detection\", bloom=\"apply\",\n",
" explanation=\"Z anti-commutes with X. A Z error on any qubit flips \\u27E8XXXX\\u27E9 from +1 to \\u22121.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"metadata": {},
"source": [
"order(tracker, \"q5_error_severity\",\n",
" instruction=\"Rank error types by how many stabilisers they trigger (fewest \\u2192 most):\",\n",
" items=[\"X\", \"Z\", \"Y\"],\n",
" correct_order=[\"X\", \"Z\", \"Y\"],\n",
" section=\"4. Error detection\", bloom=\"analyze\",\n",
" explanation=\"X \\u2192 1 (ZZZZ). Z \\u2192 1 (XXXX). Y \\u2192 2 (both). X and Z are tied at 1.\",\n",
" ties=[[\"X\", \"Z\"]])"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 5: Testing Claim (1) \u2014 The Magic Witness\n",
"\n",
"**Claim:** The magic witness $W = 1.0$, proving the encoded state fully\n",
"preserves the $T$-state character.\n",
"\n",
"The witness formula:\n",
"$$W = \\frac{1 + \\frac{\\langle X_L \\rangle + \\langle Y_L \\rangle}{\\sqrt{2}}}{2}\n",
"\\times \\frac{1 + \\langle Z_{\\text{spec}} \\rangle}{2}$$"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Measure logical operators\n",
"state = encoded_magic_statevector()\n",
"results = {}\n",
"for name, op_dict in MEASUREMENT_OPERATORS.items():\n",
" pauli_str = [\"I\"] * 4\n",
" for qubit, basis in op_dict.items():\n",
" pauli_str[qubit] = basis\n",
" label = \"\".join(reversed(pauli_str))\n",
" op = SparsePauliOp(label)\n",
" results[name] = state.expectation_value(op).real\n",
"\n",
"lx, ly, sz = results[\"logical_x\"], results[\"logical_y\"], results[\"spectator_z\"]\n",
"print(f\"<X_L> = {lx:+.6f} (ideal: +1/sqrt(2) = +{1/sqrt(2):.6f})\")\n",
"print(f\"<Y_L> = {ly:+.6f} (ideal: +1/sqrt(2) = +{1/sqrt(2):.6f})\")\n",
"print(f\"<Z_spectator> = {sz:+.6f} (ideal: +1.000000)\")\n",
"\n",
"magic_factor = (1 + (lx + ly)/sqrt(2)) / 2\n",
"spec_factor = (1 + sz) / 2\n",
"W = magic_factor * spec_factor\n",
"\n",
"print(f\"\\nMagic factor = {magic_factor:.6f}\")\n",
"print(f\"Spectator factor = {spec_factor:.6f}\")\n",
"print(f\"Witness W = {W:.6f}\")\n",
"print(f\"Library check = {logical_magic_witness(lx, ly, sz):.6f}\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Result:** $W = 1.0$. The encoding perfectly preserves the magic-state character. \\checkmark"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"quiz(tracker, \"q6_ideal_witness\",\n",
" question=\"For a perfect T-state, the magic witness W equals:\",\n",
" options=[\"0.0\", \"0.5\", \"1/\\u221A2 \\u2248 0.707\", \"1.0\"],\n",
" correct=3, section=\"5. Witness\", bloom=\"apply\",\n",
" explanation=\"Ideal: magic_factor = 1.0, spectator_factor = 1.0. Product = 1.0.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Part 6: Testing Claim (4) \u2014 Postselection Works\n",
"\n",
"**Claim:** Syndrome-based postselection correctly identifies all\n",
"detected errors. On an ideal simulator, 100% of shots have syndrome \"00\"\n",
"(no error detected)."
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"# Build the full circuit bundle and run on ideal simulator\n",
"spec = ExperimentSpec(rung=1, seed_style=\"h_p\", encoder_style=\"cx_chain\",\n",
" verification=\"both\", postselection=\"all_measured\",\n",
" shots=512, repeats=1)\n",
"bundle = build_circuit_bundle(spec)\n",
"\n",
"sim = AerSimulator()\n",
"from autoresearch_quantum.execution.analysis import summarize_context, local_memory_records\n",
"\n",
"total_accepted = 0\n",
"total_shots = 0\n",
"for name, circ in bundle.witness_circuits.items():\n",
" job = sim.run(circ, shots=512, memory=True)\n",
" memory = job.result().get_memory()\n",
" records = local_memory_records(memory, [cr.name for cr in circ.cregs])\n",
" summary = summarize_context(records, [\"z_stabilizer\", \"x_stabilizer\"],\n",
" spec.postselection, MEASUREMENT_OPERATORS[name])\n",
" total_accepted += summary[\"accepted_shots\"]\n",
" total_shots += summary[\"total_shots\"]\n",
" print(f\"{name:15s}: acceptance = {summary['acceptance_rate']:.4f}, \"\n",
" f\"<operator> = {summary['expectation']:+.4f}\")\n",
"\n",
"print(f\"\\nOverall acceptance: {total_accepted}/{total_shots} \"\n",
" f\"= {total_accepted/total_shots:.4f}\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Result:** 100% acceptance on the ideal simulator. Every shot has syndrome \"00\". \\checkmark"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"quiz(tracker, \"q7_acceptance_ideal\",\n",
" question=\"On an ideal simulator, what fraction of shots pass the syndrome check?\",\n",
" options=[\"About 50%\", \"About 75%\", \"100%\"],\n",
" correct=2, section=\"6. Postselection\", bloom=\"understand\",\n",
" explanation=\"No noise means no errors. Every shot is in the codespace, so every syndrome is 00.\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Proof Summary\n",
"\n",
"| Claim | Result | Status |\n",
"|-------|--------|--------|\n",
"| (1) Magic witness $W = 1.0$ | $W = 1.000000$ | **Proven** |\n",
"| (2) Both stabilisers at $+1$ | $\\langle XXXX \\rangle = +1$, $\\langle ZZZZ \\rangle = +1$ | **Proven** |\n",
"| (3) Every 1-qubit error detected | 12/12 detected | **Proven** |\n",
"| (4) Postselection filters correctly | 100% acceptance (ideal) | **Proven** |\n",
"\n",
"**Hypothesis H1 is confirmed.** The $[\\![4,2,2]\\!]$ code can encode a\n",
"magic state with perfect fidelity, and its error detection works exactly\n",
"as the theory predicts.\n",
"\n",
"---\n",
"\n",
"## But Wait \u2014 Next Hypothesis\n",
"\n",
"> **H2 (for Experiment 2):** Everything above was on a **perfect\n",
"> simulator** with zero noise. On a realistic noise model (mimicking\n",
"> IBM Brisbane, 127 qubits, real error rates), the magic-state quality\n",
"> will degrade \u2014 but the degradation is **quantifiable**, and by tuning\n",
"> circuit parameters we can recover significantly more magic than a\n",
"> naive default configuration.\n",
"\n",
"**The question Experiment 2 will answer:** How much magic survives\n",
"real-world noise, and can we measure the damage precisely enough to\n",
"optimise against it?"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"checkpoint_summary(tracker, \"6. Postselection\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"## Assessment"
]
},
{
"cell_type": "code",
"metadata": {},
"source": [
"tracker.dashboard()\n",
"path = tracker.save()\n",
"print(f\"\\nProgress saved to: {path}\")"
],
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"id": "d129382e",
"source": "---\n## Navigation \u2014 Plan D\n\n**\u2192 Next: [Experiment 2 \u2014 How Much Magic Survives Noise?](experiment_2_noise.ipynb)**\n\n*\u2190 Back to [Start Here](../00_START_HERE.ipynb)*",
"metadata": {}
}
]
}