autoresearch-quantum/notebooks/plan_d/experiment_1_protection.ipynb
saymrwulf 5f8b584210 Add Plan D: three claim-driven experiment notebooks
Plan D structures the material as a scientific argument chain:
- Experiment 1: Can the [[4,2,2]] code protect a magic state? (W=1.0, 12/12 errors)
- Experiment 2: How much magic survives noise? (scoring, parameter sweeps)
- Experiment 3: Can a ratchet learn to optimise? (monotonic improvement, transfer)

Each notebook follows Hypothesis → Claim → Experiment → Proof → Next Hypothesis.
Includes builder script, updated learning objectives, README, and compendium cross-ref.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 19:13:43 +02:00

561 lines
No EOL
20 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"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",
"(EastinKnill 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°$ between the\n",
"$+X$ and $+Y$ axes. Its special property: it enables the $T$-gate via\n",
"gate teleportation — 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) — 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) — 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) — 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) — 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 — 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 — 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
}
]
}