Repository navigation
docs: import old Python docs as Astro tutorials + executed notebooks #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
28fe744
docs: import old Python docs as Astro tutorials + executed notebooks
Roger-luo a6491c5
ci(docs): aggregate build steps into `npm run build` + add docs README
Roger-luo 34404e4
docs(develop): document the npm run build/extract pipeline
Roger-luo ff1b8b5
docs(examples): fix source link, drop heading bullets, label code blo…
Roger-luo 1510019
fix(docs): KaTeX was treating any parenthesised phrase as math
Roger-luo a018a60
docs: address PR #79 review comments
Roger-luo 026be11
docs: address PR #79 follow-up review comments
Roger-luo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # ppvm docs site | ||
|
|
||
| Astro site for the ppvm documentation. Deployed to gh-pages root by | ||
| `.github/workflows/docs.yml`; PRs that touch this directory get a | ||
| per-PR preview deploy. | ||
|
|
||
| ## Quick reference | ||
|
|
||
| From this directory: | ||
|
|
||
| ```bash | ||
| npm install # one-time | ||
| npm run dev # extract + astro dev → http://127.0.0.1:4321 | ||
| npm run build # extract + astro build → ./dist/ | ||
| ``` | ||
|
|
||
| `npm run dev` and `npm run build` run every prerequisite extraction | ||
| step first, so a fresh clone has one command to remember. Each | ||
| extraction step is also available on its own when you're iterating on | ||
| a specific layer: | ||
|
|
||
| | Command | What it does | | ||
| |-------------------------------|-----| | ||
| | `npm run extract:rust` | Re-run `cargo +nightly rustdoc -- --output-format json` for the three Rust crates and reshape into `src/data/rust-api.json`. Source: `scripts/extract-rust.mjs`. | | ||
| | `npm run extract:python` | Re-run `griffe dump ppvm` and reshape into `src/data/python-api.json`. Source: `scripts/extract-python.mjs`. | | ||
| | `npm run extract:notebooks` | Execute every Jupytext `.py` under `notebooks/` and emit HTML fragments + metadata to `src/generated/notebooks/`. Source: `scripts/build-notebooks.py`. | | ||
| | `npm run extract` | All three of the above, in order. | | ||
| | `npm run astro:dev` | `astro dev` without extracting anything — handy when you're editing only the Astro layer and trust the existing inputs. | | ||
| | `npm run astro:build` | `astro build` without extracting anything. | | ||
|
|
||
| ## Workflow patterns | ||
|
|
||
| - **First clone / fresh checkout:** `npm install && npm run dev`. | ||
| - **Iterating on the homepage / layout:** keep `npm run astro:dev` running; re-run `npm run extract:*` only when you change the underlying source. | ||
| - **Changed a Rust public API:** `npm run extract:rust` then refresh the browser. The site picks up the regenerated `src/data/rust-api.json` automatically. | ||
| - **Edited a notebook under `notebooks/`:** `npm run extract:notebooks`. | ||
| - **Added a *new* notebook:** drop it into `notebooks/<name>.py` as a Jupytext-percent file, then `npm run extract:notebooks`. The Examples landing page picks it up from the regenerated `src/generated/notebooks/index.json`. | ||
|
|
||
| ## Layout | ||
|
|
||
| ``` | ||
| docs/ | ||
| ├── notebooks/ # Jupytext .py — executed at build time | ||
| ├── scripts/ | ||
| │ ├── extract-rust.mjs | ||
| │ ├── extract-python.mjs | ||
| │ └── build-notebooks.py | ||
| ├── src/ | ||
| │ ├── data/ # *.json from extract:rust / extract:python (gitignored) | ||
| │ ├── generated/ # notebooks/*.html + meta + index (gitignored) | ||
| │ ├── components/, layouts/, pages/, styles/ | ||
| │ └── ... | ||
| ├── package.json | ||
| └── astro.config.mjs | ||
| ``` | ||
|
|
||
| `src/data/` and `src/generated/` are `.gitignore`'d; they're rebuilt on | ||
| every CI run via `npm run extract`. | ||
|
|
||
| ## CI | ||
|
|
||
| `.github/workflows/docs.yml` builds the site via `npm run build` on | ||
| every PR that touches `docs/`, `crates/`, `ppvm-python/`, or itself. | ||
| PR previews land at `gh-pages/pr-preview/pr-<N>/`; the main deploy | ||
| publishes to `gh-pages` root on every push to `main`. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node ≥ 20 (Astro 5 requirement). | ||
| - `uv` (https://docs.astral.sh/uv/) — used to drive the Python | ||
| notebook executor and the griffe-based Python extractor. | ||
| - Rust nightly — `extract-rust.mjs` invokes `cargo +nightly rustdoc | ||
| --output-format json`. Install with `rustup toolchain install | ||
| nightly`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| # --- | ||
| # jupyter: | ||
| # jupytext: | ||
| # cell_metadata_filter: -all | ||
| # custom_cell_magics: kql | ||
| # text_representation: | ||
| # extension: .py | ||
| # format_name: percent | ||
| # format_version: '1.3' | ||
| # jupytext_version: 1.19.1 | ||
| # kernelspec: | ||
| # display_name: ppvm (3.12.12) | ||
| # language: python | ||
| # name: python3 | ||
| # --- | ||
|
|
||
| # %% [markdown] | ||
| # # Magic State Distillation with the Generalized Stabilizer Tableau | ||
| # | ||
| # Here we simulate an 85-qubit MSD circuit (5 code blocks of 17 qubits each) using ppvm's | ||
| # `GeneralizedTableau`. It is loosely based on [the TSIM example](https://bloqade.quera.com/latest/digital/examples/tsim/magic_state_distillation/), | ||
| # but with fewer details. | ||
|
|
||
| # %% | ||
| import time | ||
|
|
||
| from ppvm import GeneralizedTableau, MeasurementResult | ||
|
|
||
| QUBITS_PER_CODE_BLOCK = 17 | ||
|
|
||
|
|
||
| def encode(tab: GeneralizedTableau, qubits: list[int]) -> None: | ||
| """Apply the 17-qubit surface code encoding circuit.""" | ||
| for i in [0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16]: | ||
| tab.sqrt_y(qubits[i]) | ||
|
|
||
| for i, j in [[1, 3], [7, 10], [12, 14], [13, 16]]: | ||
| tab.cz(qubits[i], qubits[j]) | ||
| for i in [7, 16]: | ||
| tab.sqrt_y_adj(qubits[i]) | ||
| for i, j in [[4, 7], [8, 10], [11, 14], [15, 16]]: | ||
| tab.cz(qubits[i], qubits[j]) | ||
| for i in [4, 10, 14, 16]: | ||
| tab.sqrt_y_adj(qubits[i]) | ||
| for i, j in [[2, 4], [6, 8], [7, 9], [10, 13], [14, 16]]: | ||
| tab.cz(qubits[i], qubits[j]) | ||
| for i in [3, 6, 9, 10, 12, 13]: | ||
| tab.sqrt_y(qubits[i]) | ||
| for i, j in [[0, 2], [3, 6], [5, 8], [10, 12], [11, 13]]: | ||
| tab.cz(qubits[i], qubits[j]) | ||
| for i in [1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 14]: | ||
| tab.sqrt_y(qubits[i]) | ||
| for i, j in [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [12, 15]]: | ||
| tab.cz(qubits[i], qubits[j]) | ||
| for i in [0, 2, 5, 6, 8, 10, 12]: | ||
| tab.sqrt_y_adj(qubits[i]) | ||
|
|
||
|
|
||
| def msd_circuit(tab: GeneralizedTableau) -> list[MeasurementResult]: | ||
| """Build and measure the full 85-qubit MSD circuit.""" | ||
| n_qubits = QUBITS_PER_CODE_BLOCK * 5 | ||
| qubit_addrs = list(range(n_qubits)) | ||
|
|
||
| # Split into 5 code blocks | ||
| ql = [ | ||
| qubit_addrs[i * QUBITS_PER_CODE_BLOCK : (i + 1) * QUBITS_PER_CODE_BLOCK] | ||
| for i in range(5) | ||
| ] | ||
|
|
||
| # Prepare magic state in each block: H + T on encoding qubit, then encode | ||
| for q in ql: | ||
| encoding_qubit = q[7] | ||
| tab.h(encoding_qubit) | ||
| tab.t(encoding_qubit) | ||
| encode(tab, q) | ||
|
|
||
| # Cross-block entangling operations | ||
| for i in [0, 1, 4]: | ||
| for q in ql[i]: | ||
| tab.sqrt_x(q) | ||
|
|
||
| for control, target in zip(ql[0], ql[1]): | ||
| tab.cz(control, target) | ||
| for control, target in zip(ql[2], ql[3]): | ||
| tab.cz(control, target) | ||
|
|
||
| for q in ql[0]: | ||
| tab.sqrt_y(q) | ||
| for q in ql[3]: | ||
| tab.sqrt_y(q) | ||
|
|
||
| for control, target in zip(ql[0], ql[2]): | ||
| tab.cz(control, target) | ||
| for control, target in zip(ql[3], ql[4]): | ||
| tab.cz(control, target) | ||
|
|
||
| for q in ql[0]: | ||
| tab.sqrt_x_adj(q) | ||
|
|
||
| for control, target in zip(ql[0], ql[4]): | ||
| tab.cz(control, target) | ||
| for control, target in zip(ql[1], ql[3]): | ||
| tab.cz(control, target) | ||
|
|
||
| for i in range(5): | ||
| for q in ql[i]: | ||
| tab.sqrt_x_adj(q) | ||
|
|
||
| # Measure all qubits | ||
| return [tab.measure(i) for i in range(n_qubits)] | ||
|
|
||
|
|
||
| # %% [markdown] | ||
| # ## Running the circuit | ||
| # | ||
| # Each shot requires its own copy of the initial tableau since measurement mutates the state. | ||
| # We use `fork()` to create independent copies with separate RNG streams. | ||
|
|
||
| # %% | ||
| n_qubits = QUBITS_PER_CODE_BLOCK * 5 | ||
| n_shots = 1000 | ||
|
|
||
| tab = GeneralizedTableau(n_qubits) | ||
|
|
||
| start = time.perf_counter() | ||
|
|
||
| results = [] | ||
| for shot in range(n_shots): | ||
| tab_shot = tab.fork(seed=shot) | ||
| results.append(msd_circuit(tab_shot)) | ||
|
|
||
| elapsed = time.perf_counter() - start | ||
|
|
||
| print(f"Simulated {n_shots} shots of the {n_qubits}-qubit MSD circuit") | ||
| print(f"Total time: {elapsed:.2f} s ({elapsed / n_shots * 1e3:.2f} ms per shot)") | ||
|
|
||
| # %% [markdown] | ||
| # Let's look at the measurement outcomes. | ||
|
|
||
| # %% | ||
| bitstrings = [ | ||
| "".join("1" if r == MeasurementResult.ONE else "0" for r in shot) | ||
| for shot in results | ||
| ] | ||
|
Roger-luo marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.