Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 304 additions & 0 deletions docs/demos/global_rotation_steane.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "03a94646",
"metadata": {},
"source": [
"# Global rotations of the Steane code\n",
"\n",
"This tutorial reproduces the two-dimensional color-code part of Fig. 4a from [Bluvstein et al., *Nature* 649, 39-46 (2026)](https://www.nature.com/articles/s41586-025-09848-5) with Tsim. The experiment prepares a small error-correcting code, applies the same physical phase rotation to every data qubit, and then asks which rotation angles preserve the code space and the logical observable.\n",
"\n",
"The `[[7, 1, 3]]` Steane code is the required 2D color-code curve from the unitaryHACK issue. The `[[15, 1, 3]]` Reed-Muller curve is not included here because it requires many simultaneous non-Clifford rotations and the issue marks it as optional when it is not practical to run quickly."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "59e6bdcd",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"from tsim import Circuit"
]
},
{
"cell_type": "markdown",
"id": "9e85ad09",
"metadata": {},
"source": [
"## Encode a logical qubit\n",
"\n",
"The Steane encoder below is the deterministic `[[7, 1, 3]]` circuit already used in the Tsim color-code demo. It prepares a logical `|+_L>` state. We measure six stabilizer generators and one logical `X_L` operator after applying the global rotation.\n",
"\n",
"One X-type stabilizer is written with a leading `!` because this particular deterministic encoder prepares that generator with negative sign. The inversion normalizes all six detector columns so that a zero detector bit means \"the expected stabilizer value was recovered.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e29d976e",
"metadata": {},
"outputs": [],
"source": [
"STEANE_ENCODER = \"\"\"\n",
"RX 6\n",
"R 0 1 2 3 4 5\n",
"SQRT_Y_DAG 0 1 2 3 4 5\n",
"CZ 1 2 3 4 5 6\n",
"SQRT_Y 6\n",
"CZ 0 3 2 5 4 6\n",
"SQRT_Y 2 3 4 5 6\n",
"CZ 0 1 2 3 4 5\n",
"SQRT_Y 1 2 4\n",
"X 3\n",
"\"\"\"\n",
"\n",
"X_STABILIZERS = [\n",
" \"!X0*X1*X2*X3\",\n",
" \"X1*X2*X4*X5\",\n",
" \"X2*X3*X4*X6\",\n",
"]\n",
"Z_STABILIZERS = [\n",
" \"Z0*Z1*Z2*Z3\",\n",
" \"Z1*Z2*Z4*Z5\",\n",
" \"Z2*Z3*Z4*Z6\",\n",
"]\n",
"LOGICAL_X = \"X0*X1*X5\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a8ea488f",
"metadata": {},
"outputs": [],
"source": [
"def make_steane_rotation_circuit(theta_over_pi: float) -> Circuit:\n",
" pauli_measurements = \" \".join(X_STABILIZERS + Z_STABILIZERS + [LOGICAL_X])\n",
" return Circuit(f\"\"\"\n",
"{STEANE_ENCODER}\n",
"R_Z({theta_over_pi}) 0 1 2 3 4 5 6\n",
"MPP {pauli_measurements}\n",
"DETECTOR rec[-7]\n",
"DETECTOR rec[-6]\n",
"DETECTOR rec[-5]\n",
"DETECTOR rec[-4]\n",
"DETECTOR rec[-3]\n",
"DETECTOR rec[-2]\n",
"OBSERVABLE_INCLUDE(0) rec[-1]\n",
"\"\"\")\n",
"\n",
"circuit = make_steane_rotation_circuit(0.25)\n",
"print(f\"Qubits: {circuit.num_qubits}\")\n",
"print(f\"Detectors: {circuit.num_detectors}\")\n",
"print(f\"Observables: {circuit.num_observables}\")"
]
},
{
"cell_type": "markdown",
"id": "9c57632b",
"metadata": {},
"source": [
"The global `R_Z(theta)` instruction rotates every physical data qubit by `theta * pi`. Tsim then samples the stabilizer detectors and logical observable directly from the non-Clifford circuit."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "af57b168",
"metadata": {},
"outputs": [],
"source": [
"circuit.diagram(\"timeline-svg\", height=360)"
]
},
{
"cell_type": "markdown",
"id": "80c94d6d",
"metadata": {},
"source": [
"## Sweep the rotation angle\n",
"\n",
"For each angle, we estimate three quantities:\n",
"\n",
"- the mean stabilizer expectation, averaged over the six measured generators;\n",
"- the raw logical `X_L` expectation;\n",
"- the postselected logical `X_L` expectation using only shots with all stabilizers restored.\n",
"\n",
"The last column is useful as a tutorial proxy for the postselection used in the paper: it shows the logical response conditioned on returning to the expected code space."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9ac04651",
"metadata": {},
"outputs": [],
"source": [
"def bit_expectation(bits: np.ndarray) -> np.ndarray:\n",
" return 1 - 2 * bits.mean(axis=0)\n",
"\n",
"\n",
"def evaluate_steane(theta_over_pi: float, shots: int = 1024) -> dict[str, float]:\n",
" sampler = make_steane_rotation_circuit(theta_over_pi).compile_detector_sampler(seed=1234)\n",
" detectors, observables = sampler.sample(\n",
" shots=shots,\n",
" batch_size=shots,\n",
" separate_observables=True,\n",
" )\n",
" postselection_mask = np.all(detectors == 0, axis=1)\n",
" if np.any(postselection_mask):\n",
" postselected_logical = bit_expectation(observables[postselection_mask, 0])[()]\n",
" else:\n",
" postselected_logical = np.nan\n",
"\n",
" return {\n",
" \"theta_over_pi\": theta_over_pi,\n",
" \"stabilizer\": float(bit_expectation(detectors).mean()),\n",
" \"logical_x\": float(bit_expectation(observables[:, 0])),\n",
" \"postselected_logical_x\": float(postselected_logical),\n",
" \"acceptance\": float(postselection_mask.mean()),\n",
" }\n",
"\n",
"\n",
"angles = np.linspace(0, 1, 17)\n",
"results = [evaluate_steane(float(theta)) for theta in angles]\n",
"results[:3]"
]
},
{
"cell_type": "markdown",
"id": "2240ea4c",
"metadata": {},
"source": [
"## Add an unentangled reference\n",
"\n",
"Fig. 4a also compares encoded data to an unentangled experiment. Here we prepare seven independent `|+>` qubits, apply the same global rotation, and measure the same weight-three `X` parity used as the logical reference. It is not protected by stabilizers, so it changes smoothly with the physical rotation angle."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9cf444a1",
"metadata": {},
"outputs": [],
"source": [
"def make_unentangled_reference(theta_over_pi: float) -> Circuit:\n",
" return Circuit(f\"\"\"\n",
"RX 0 1 2 3 4 5 6\n",
"R_Z({theta_over_pi}) 0 1 2 3 4 5 6\n",
"MPP X0*X1*X5\n",
"OBSERVABLE_INCLUDE(0) rec[-1]\n",
"\"\"\")\n",
"\n",
"\n",
"def evaluate_reference(theta_over_pi: float, shots: int = 1024) -> float:\n",
" sampler = make_unentangled_reference(theta_over_pi).compile_detector_sampler(seed=5678)\n",
" _, observables = sampler.sample(\n",
" shots=shots,\n",
" batch_size=shots,\n",
" separate_observables=True,\n",
" )\n",
" return float(bit_expectation(observables[:, 0]))\n",
"\n",
"\n",
"reference_logical = np.array([evaluate_reference(float(theta)) for theta in angles])"
]
},
{
"cell_type": "markdown",
"id": "64ac0bd2",
"metadata": {},
"source": [
"## Plot the reproduced curve\n",
"\n",
"The encoded stabilizers revive at Clifford-protected angles, while the unentangled reference lacks those code-space revivals. The postselected logical curve highlights the plateau behavior after keeping only shots whose stabilizers match the expected signs."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "20f84e15",
"metadata": {},
"outputs": [],
"source": [
"theta_degrees = angles * 180\n",
"stabilizer = np.array([row[\"stabilizer\"] for row in results])\n",
"logical_x = np.array([row[\"logical_x\"] for row in results])\n",
"postselected_logical_x = np.array([row[\"postselected_logical_x\"] for row in results])\n",
"acceptance = np.array([row[\"acceptance\"] for row in results])\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 4.5))\n",
"ax.plot(theta_degrees, stabilizer, \"o-\", label=\"Steane stabilizers\")\n",
"ax.plot(theta_degrees, logical_x, \"o-\", label=\"Steane logical $X_L$\")\n",
"ax.plot(theta_degrees, postselected_logical_x, \"o-\", label=\"Postselected $X_L$\")\n",
"ax.plot(theta_degrees, reference_logical, \"--\", label=\"Unentangled reference\")\n",
"\n",
"for angle in (0, 90, 180):\n",
" ax.axvline(angle, color=\"0.85\", lw=0.8, zorder=0)\n",
"\n",
"ax.set_xlabel(r\"Global phase rotation $\theta$ (degrees)\")\n",
"ax.set_ylabel(\"Expectation value\")\n",
"ax.set_ylim(-1.05, 1.05)\n",
"ax.set_xticks([0, 45, 90, 135, 180])\n",
"ax.legend(loc=\"best\")\n",
"ax.grid(alpha=0.25)\n",
"fig.tight_layout();"
]
},
{
"cell_type": "markdown",
"id": "580ac8c6",
"metadata": {},
"source": [
"The acceptance fraction peaks when the global rotation maps the code space back onto itself. Away from those angles, stabilizer measurements detect that the state has leaked out of the expected code sector more often."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e672582a",
"metadata": {},
"outputs": [],
"source": [
"fig, ax = plt.subplots(figsize=(7, 2.8))\n",
"ax.plot(theta_degrees, acceptance, \"o-\", color=\"tab:green\")\n",
"ax.set_xlabel(r\"Global phase rotation $\theta$ (degrees)\")\n",
"ax.set_ylabel(\"Code-space acceptance\")\n",
"ax.set_ylim(0, 1.05)\n",
"ax.set_xticks([0, 45, 90, 135, 180])\n",
"ax.grid(alpha=0.25)\n",
"fig.tight_layout();"
]
},
{
"cell_type": "markdown",
"id": "643c487b",
"metadata": {},
"source": [
"## What Tsim computed\n",
"\n",
"Tsim compiled the non-Clifford global rotations and sampled detector/observable outcomes from the resulting Clifford+rotation circuit. The stabilizer detectors are parity checks that indicate whether the encoded state returned to the expected Steane code sector. The logical observable estimates the encoded qubit response to the global physical rotation.\n",
"\n",
"This is the same mechanism emphasized in Fig. 4a: encoded states respond differently from unentangled physical qubits because the stabilizer structure filters which physical rotations act like robust logical operations."
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ nav:
- Contributing: contrib.md
- Tutorials:
- demos/encoding_demo.ipynb
- demos/global_rotation_steane.ipynb
- demos/magic_state_distillation.ipynb
- demos/from_stim_to_tsim.ipynb
- API Reference: reference/
Expand Down
Loading