From f133710d4e8019d719662e9fbe3dc015b0b274a7 Mon Sep 17 00:00:00 2001 From: David Plankensteiner Date: Thu, 26 Mar 2026 14:41:42 +0100 Subject: [PATCH 01/10] Add bloqade-lanes to the documentation build --- .github/pull_bloqade_submodules/action.yml | 15 +++++++++++++++ .github/workflows/dev-doc-nightly.yml | 9 ++++++++- docs/reference/index.md | 1 + docs/scripts/gen_ref_nav.py | 13 +++++++++++++ mkdocs.yml | 1 + pyproject.toml | 3 ++- 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/.github/pull_bloqade_submodules/action.yml b/.github/pull_bloqade_submodules/action.yml index db7faf2e2..d2ff78906 100644 --- a/.github/pull_bloqade_submodules/action.yml +++ b/.github/pull_bloqade_submodules/action.yml @@ -32,3 +32,18 @@ runs: repository: QuEraComputing/bloqade-analog path: 'submodules/bloqade-analog' ref: ${{ steps.bloqade_analog_extract_version.outputs.version }} + + - name: extract bloqade-lanes version + id: bloqade_lanes_extract_version + shell: bash + run: | + VERSION=$(uv pip show bloqade-lanes | awk '/^Version: / {print "v"$2}') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: clone the latest bloqade-lanes release + id: clone_bloqade_lanes + uses: actions/checkout@v5 + with: + repository: QuEraComputing/bloqade-lanes + path: 'submodules/bloqade-lanes' + ref: ${{ steps.bloqade_lanes_extract_version.outputs.version }} diff --git a/.github/workflows/dev-doc-nightly.yml b/.github/workflows/dev-doc-nightly.yml index 898895ebc..0ac5218f2 100644 --- a/.github/workflows/dev-doc-nightly.yml +++ b/.github/workflows/dev-doc-nightly.yml @@ -32,6 +32,13 @@ jobs: repository: QuEraComputing/bloqade-circuit path: 'submodules/bloqade-circuit' + - name: clone the latest bloqade-lanes + id: clone_bloqade_lanes + uses: actions/checkout@v6 + with: + repository: QuEraComputing/bloqade-lanes + path: 'submodules/bloqade-lanes' + - name: clone the latest bloqade-analog id: clone_bloqade_analog uses: actions/checkout@v6 @@ -40,7 +47,7 @@ jobs: path: 'submodules/bloqade-analog' - name: add local repos as dependencies - run: uv add submodules/bloqade-circuit submodules/bloqade-analog + run: uv add submodules/bloqade-circuit submodules/bloqade-lanes submodules/bloqade-analog - name: Set up build cache uses: actions/cache@v5 diff --git a/docs/reference/index.md b/docs/reference/index.md index 31a5f5e0a..af56af60e 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -3,6 +3,7 @@ Here you can find a full API reference grouped by the respective submodules of bloqade: * [`bloqade-circuit`](./bloqade-circuit/src/bloqade/types) +* [`bloqade-lanes`](./bloqade-lanes/python/bloqade/lanes/device) * [`bloqade-analog`](./bloqade-analog/src/bloqade/analog/) Please note that the reference is auto-generated and therefore follows the structure of the code. diff --git a/docs/scripts/gen_ref_nav.py b/docs/scripts/gen_ref_nav.py index 517b8d644..e8fa5fde1 100644 --- a/docs/scripts/gen_ref_nav.py +++ b/docs/scripts/gen_ref_nav.py @@ -8,6 +8,7 @@ if os.getenv("GITHUB_ACTIONS") == "true": BLOQADE_CIRCUIT_SRC_PATH = "submodules/bloqade-circuit/" BLOQADE_ANALOG_SRC_PATH = "submodules/bloqade-analog/" + BLOQADE_LANES_SRC_PATH = "submodules/bloqade-lanes/" else: """ NOTE: we assume the following project structure when building locally: @@ -15,9 +16,11 @@ ../ ├── bloqade ├── bloqade-analog + ├── bloqade-lanes └── bloqade-circuit """ BLOQADE_CIRCUIT_SRC_PATH = "../bloqade-circuit/" + BLOQADE_LANES_SRC_PATH = "../bloqade-lanes/" BLOQADE_ANALOG_SRC_PATH = "../bloqade-analog/" @@ -41,6 +44,12 @@ "docs/", "debug/", "squin/cirq/emit/", # NOTE: this fails when included because there is an __init__.py missing, but the files have no docs anyway and it will be moved so safe to ignore + "demo/", + "scripts/", + "examples/", + "crates/", + "lanes/analysis/", # TODO: don't skip this + "lanes/arch/gemini/", ] @@ -101,3 +110,7 @@ def make_nav(bloqade_package_name: str, BLOQADE_PACKAGE_PATH: str): bloqade_analog_nav = make_nav("bloqade-analog", BLOQADE_ANALOG_SRC_PATH) with mkdocs_gen_files.open("reference/SUMMARY_BLOQADE_ANALOG.md", "w") as nav_file: nav_file.writelines(bloqade_analog_nav.build_literate_nav()) + +bloqade_lanes_nav = make_nav("bloqade-lanes", BLOQADE_LANES_SRC_PATH) +with mkdocs_gen_files.open("reference/SUMMARY_BLOQADE_LANES.md", "w") as nav_file: + nav_file.writelines(bloqade_lanes_nav.build_literate_nav()) diff --git a/mkdocs.yml b/mkdocs.yml index 524073c4f..eec4ec6e4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,7 @@ nav: - API Reference: - 'reference/index.md' - Bloqade Digital: 'reference/bloqade-circuit/src/bloqade/' + - Bloqade Lanes: 'reference/bloqade-lanes/python/bloqade/' - Bloqade Analog: 'reference/bloqade-analog/src/bloqade/analog/' - Blog: - blog/index.md diff --git a/pyproject.toml b/pyproject.toml index a39680a36..c6e73ea9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,8 +20,9 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ - "bloqade-circuit[cirq,qasm2,qbraid,vis,stim]~=0.11.0", + "bloqade-circuit[cirq,qasm2,qbraid,vis,stim]~=0.14.0", "bloqade-analog~=0.16.3", + "bloqade-lanes~=0.6.0", ] [build-system] From c36fa70b6f0716a561242dd27febed066daa5c0f Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 26 Mar 2026 11:07:37 -0400 Subject: [PATCH 02/10] docs: added simulator device demo notebook to nav on digital examples --- .../digital/examples/gemini_logical/image.svg | 10 + .../gemini_logical/simulator_device_demo.py | 453 ++++++++++++++++++ mkdocs.yml | 2 + 3 files changed, 465 insertions(+) create mode 100644 docs/digital/examples/gemini_logical/image.svg create mode 100644 docs/digital/examples/gemini_logical/simulator_device_demo.py diff --git a/docs/digital/examples/gemini_logical/image.svg b/docs/digital/examples/gemini_logical/image.svg new file mode 100644 index 000000000..0c9ddcec2 --- /dev/null +++ b/docs/digital/examples/gemini_logical/image.svg @@ -0,0 +1,10 @@ + + + + diff --git a/docs/digital/examples/gemini_logical/simulator_device_demo.py b/docs/digital/examples/gemini_logical/simulator_device_demo.py new file mode 100644 index 000000000..eacdde60b --- /dev/null +++ b/docs/digital/examples/gemini_logical/simulator_device_demo.py @@ -0,0 +1,453 @@ +# --- +# jupyter: +# jupytext: +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.19.1 +# kernelspec: +# display_name: kirin-workspace (3.12.13) +# language: python +# name: python3 +# --- + +# %% [markdown] +# ## Simulator demo for Gemini logical +# Gemini logical is a specific set of dialects and compilers in bloqade. This demo is hosted in the `bloqade-lanes` repo, which is where the move synthesis compiler lives. + +# %% +# Builtins +import math +from collections import Counter +import numpy as np + +# Types +from typing import Any +from kirin.dialects import ilist +from kirin.ir.method import Method + +# Functions and methods +from bloqade.decoders import BpLsdDecoder +from bloqade.lanes import GeminiLogicalSimulator + +# Dialect groups +from bloqade.gemini import logical +from bloqade import qubit, squin + +# %% + +import matplotlib.pyplot as plt + + +def render_steane_code_qubit( + ax: plt.Axes | None = None, center: tuple[float, float] = (0, 0) +) -> plt.Axes: + if ax is None: + fig, ax = plt.subplots() + ax.set_aspect("equal") + ax.set_xlim([-2 + center[0], 2 + center[0]]) + ax.set_ylim([-2 + center[1], 2 + center[1]]) + ax.axis("off") + RED = "#EF2F55" + PURPLE = "#670EFF" + GREEN = "#57BC13" + + pos_center = np.zeros([2, 7]) + pos_center[:, 1::] = np.array( + [ + np.cos(np.linspace(0, 2 * np.pi, 7)[0:6]) * 1.5, + 1.5 * np.sin(np.linspace(0, 2 * np.pi, 7)[0:6]), + ] + ) + pos_center += np.array(center).reshape(2, 1) + + ax.scatter( + pos_center[0], pos_center[1], color="w", s=800, zorder=100, edgecolors="k" + ) + indexing = [2, 0, 3, 6, 4, 5, 1] + for xi, yi, label in zip(pos_center[0], pos_center[1], indexing): + ax.text(xi, yi, str(label), ha="center", va="center", zorder=200) + + ax.fill( + [pos_center[0, x] for x in [0, 2, 3, 4]], + [pos_center[1, x] for x in [0, 2, 3, 4]], + color=RED, + ) + ax.fill( + [pos_center[0, x] for x in [0, 4, 5, 6]], + [pos_center[1, x] for x in [0, 4, 5, 6]], + color=GREEN, + ) + ax.fill( + [pos_center[0, x] for x in [0, 6, 1, 2]], + [pos_center[1, x] for x in [0, 6, 1, 2]], + color=PURPLE, + ) + logical_label = [indexing.index(5), indexing.index(1), indexing.index(0)] + ax.plot( + pos_center[0, logical_label], + pos_center[1, logical_label], + color="k", + ls="-", + linewidth=5, + zorder=50, + ) + + return ax + + +# A minimal kernel that prepares a single qubit in an arbitrary state, +# so that it can be shown by the tsim renderer. +@logical.kernel(aggressive_unroll=True, verify=True) +def main(): + reg = qubit.qalloc(1) + squin.u3(0.1, 0.2, 0.3, reg[0]) + return logical.terminal_measure(reg) + + +task = GeminiLogicalSimulator().task(main) + +# %% [markdown] +# ### Some prototype stdutils functions: detectors and observables +# We break abstraction a bit between physical and logical qubits. Each logical measurement is a batch of 7 physical measurements as indexed by the following diagram. +# +# In order to correct the errors from a Steane code, we need to inform the decoder and detector error model of the decoding steps. This can be done by defining the **detectors** and the **observables**. +# +# For the Steane code, the detectors are four-qubit parity patches corresponding to the three plaquettes of the code; in the following render the default detectors are red/green/purple patches. +# +# For the Steane code, the obsevables are three-qubit parity lines corresponding to edges of the code; in the following render is default observable are the black line. + +# %% +render_steane_code_qubit() +task.tsim_circuit.diagram(width=400) + + +# %% [markdown] +# Lets define some default functions which use the `squin.set_detector` and `squin.set_observable` functions, which annotate the program for later analysis to generate the detector error model. + +# %% [markdown] +# For the purposes of our demonstration, lets prepare a simple GHZ state. Note that the decorator is `@logical.kernel` instead of `@squin.kernel`. + + +# %% +@logical.kernel(aggressive_unroll=True, verify=True) +def main(): + reg = qubit.qalloc(3) + squin.h(reg[0]) + squin.cx(reg[0], reg[1]) + + return logical.default_post_processing(reg) # Return the physical measurements + + +task = GeminiLogicalSimulator().task(main) + +# %% [markdown] +# The `task` has several attributes. The key attributes are: +# | Attribute | Description | +# |-------------------|--------------------------------------| +# | `task.run` | Run the task, sampling bitstrings from the noisy distribution | +# | `task.noiseless_tsim_circuit` | The underlying physical circuit without noise | +# | `task.tsim_circuit` | The underlying physical circuit including noise | +# | `task.detector_error_model` | The DEM associated with the noisy circuit | +# | `task.visualize` | Render an interactive atom move. Does not work in jupyter notebooks =( | +# +# ## Rendering of the noiseless circuit: + +# %% +task.noiseless_tsim_circuit.diagram(height=task.noiseless_tsim_circuit.num_qubits * 25) + +# %% [markdown] +# ## Rendering of the noisy circuit: +# Includes 1 and 2 qubit gate error, spectator errors, and move errors. It does not include state preparation errors. + +# %% +task.tsim_circuit.diagram(height=task.tsim_circuit.num_qubits * 25) + +# %% [markdown] +# ## Running the task +# the `task.run` attribute compiles the task to tsim and then samples from it. Note that the majority of the time is spent compiling the task; the sampler is very fast. + +# %% +result = task.run(1, with_noise=True) +result_wo_noise = task.run(1, with_noise=False) + +# %% +# After recompilation, the task runs very quickly. +result = task.run(10000, with_noise=True) +result_wo_noise = task.run(10000, with_noise=False) + +# %% [markdown] +# The `result` object has several meaningful attributes that are useful for analysis: +# | Attribute | Description | +# |-------------------|--------------------------------------| +# | `result.return_values` | The values returned from the kernel | +# | `result.detectors` | The parity values of the annotated detectors | +# | `result.observables` | The parity values of the annotated observables | +# | `result.physical` | The physical qubit measurements | +# +# For each value, the zeroth dimension is the shot index. +# - `detectors` are a flattened list of `[ [detectors of qubit 0 ], [detectors of qubit 1] [ ... ] ]` +# - `observables` are a list of `[ obsevable of qubit 0, observable of qubit 1, ... ]` +# - `physical` is a nested list of `[[7 physical measurements of qubit 0], [7 physical measurements of qubit 1], ...]` +# +# Indexing is in the same ordering of however the qubits were measured in the `logical.terminal_measure` statement. + +# %% +return_values = result.return_values +detectors = np.asarray(result.detectors) +observables = np.asarray(result.observables) +physical = np.asarray(result.measurements) +observables_without_noise = np.asarray(result_wo_noise.observables) + +print(detectors.shape) +print(observables.shape) +print(physical.shape) + +# %% [markdown] +# ## Decoding and post-selection +# Decoders can be inherited from elsewhere but follow a common pattern. Given the detector error model, flips to the logical qubits can be decoded based on the detector triggers. Because the code is linear, the corrected code is simply the XOR of the flips and the observables. +# +# Alternatively, one may postselect on having no errors, or all detectors being zero. + +# %% +# Correct +flips = BpLsdDecoder(task.detector_error_model).decode(detectors) +observables_corrected = observables ^ flips +print("Average bits flipped:", np.average(flips)) + +# Postselect +post_selection = np.all(detectors == 0, axis=1) +observables_postselected = observables[post_selection, :] +print("Postselection rate: ", len(observables_postselected) / len(observables)) + +# %% [markdown] +# ## Analysis 1: parity +# For the GHZ state, we have the convenience of the final state being uniformly sampled from 00 or 11, with 01 or 10 indicating an error outside of the distribution. Thus, computing the parity of the observables can serve as a proxy of the fidelity of the distribution: parity 0 means no error, parity 1 means error, and the average parity is the error rate. Postselection and correction decreases the parity, meaning the final error is better! + +# %% +print( + "Average parity (before correction):", + np.average(observables[:, 0] ^ observables[:, 1]), +) +print( + "Average parity (after correction):", + np.average(observables_corrected[:, 0] ^ observables_corrected[:, 1]), +) +print( + "Average parity (after postselection):", + np.average(observables_postselected[:, 0] ^ observables_postselected[:, 1]), +) + + +# %% [markdown] +# ### Some helper functions and standard utilities to analyze statistical divergence + + +# %% +# helper functions to analyze statistical distribution of logical measurements +def get_hist(obs_array: np.ndarray): + return Counter(map(lambda x: tuple(map(int, x)), obs_array[:])) + + +def kl_divergence(p_hist: Counter, q_hist: Counter) -> float: + """Compute the KL divergence D_KL(P || Q) between two histograms.""" + total_p = sum(p_hist.values()) + total_q = sum(q_hist.values()) + if total_p == 0 or total_q == 0: + return float("inf") # Infinite divergence if one distribution is empty + divergence = 0.0 + for key in p_hist: + p_prob = p_hist[key] / total_p + q_prob = q_hist.get(key, 0) / total_q + if q_prob > 0: + divergence += p_prob * math.log(p_prob / q_prob) + else: + divergence += p_prob * math.log(p_prob / (1e-10)) # Avoid log(0) + return divergence + + +# %% [markdown] +# The Kullback-Leibler divergence $D_{KL}(P||Q)$ measures the dissimilarity between two probability distributions. When the KL divergence is zero, there is no loss when the noisy distribution (Q) is used to represent the perfect distribution (P). Similar to the parity measurement above, we find that the divergence is lower for corrected and postselected distributions. Note that the distribution is approximated from finite sampling (a simple frequentist bootstrap) so the KL divergence is an upper bound on the true distribution. + +# %% +observables_hist = get_hist(observables) +observables_decoded_hist = get_hist(observables_corrected) +observables_postselected_hist = get_hist(observables_postselected) +observables_wo_noise_hist = get_hist(observables_without_noise) + +# compute and print the KL divergence between the histograms +print( + "KL divergence between noiseless and raw observables:", + kl_divergence(observables_wo_noise_hist, observables_hist), +) +print( + "KL divergence between noiseless and decoded observables:", + kl_divergence(observables_wo_noise_hist, observables_decoded_hist), +) +print( + "KL divergence between noiseless and post-selected observables:", + kl_divergence(observables_wo_noise_hist, observables_postselected_hist), +) + + +# %% [markdown] +# ### Dos and do nots for kernels +# A valid kernel for Gemini must: +# 1. Have less than 10 qubits +# 2. Only have a single non-Clifford gate per qubit, acting as a single-qubit gate as the first gate on each qubit +# 3. Measurement is in Z basis only. +# +# ![image.svg](../image.svg) + +# %% [markdown] +# Too many qubits + +# %% +try: + + @logical.kernel(aggressive_unroll=True, verify=True) + def main(): + reg = qubit.qalloc(12) + squin.h(reg[0]) + squin.cx(reg[0], reg[1]) + + return logical.default_post_processing(reg) + + task = GeminiLogicalSimulator().task(main) +except BaseException as e: + print("Error during kernel definition or task creation:", e) + +# %% [markdown] +# Repeated non-Clifford rotations + +# %% +try: + + @logical.kernel(aggressive_unroll=True, verify=True) + def main(): + reg = qubit.qalloc(12) + squin.t(reg[0]) + squin.t(reg[0]) + squin.cx(reg[0], reg[1]) + + return logical.default_post_processing(reg) + + task = GeminiLogicalSimulator().task(main) +except BaseException as e: + print("Error during kernel definition or task creation:", e) + +# %% [markdown] +# Non-Clifford rotation not as the first gate (This is the same validation error) + +# %% +try: + + @logical.kernel(aggressive_unroll=True, verify=True) + def main(): + reg = qubit.qalloc(12) + squin.h(reg[0]) + squin.cx(reg[0], reg[1]) + squin.t(reg[0]) + + return logical.default_post_processing(reg) + + task = GeminiLogicalSimulator().task(main) +except BaseException as e: + print("Error during kernel definition or task creation:", e) + + +# %% [markdown] +# ## Explicitly annotate parallelism +# If parallelism is not annotated, the program will implement each two qubit gate sequentially. We currently do not have any auto-parallelization passes. + + +# %% +def terminal_main_wrapper(kernel: Method[[], ilist.IList[qubit.Qubit, Any]]) -> Method: + """ + A helper function that wraps a kernel that returns a qubit register that has + had some computation performed on it and transforms it into a logical kernel. + """ + + @logical.kernel(aggressive_unroll=True, verify=True) + def terminal_main(): + reg = kernel() + + logical.default_post_processing(reg) + + return terminal_main + + +# %% +@squin.kernel +def unparallelized_main() -> ilist.IList[qubit.Qubit, Any]: + """ + A kernel that annotates no parallelism, even though they exist + """ + reg = qubit.qalloc(4) + squin.cx(reg[0], reg[1]) + squin.cx(reg[2], reg[3]) + return reg + + +@squin.kernel +def parallelized_main() -> ilist.IList[qubit.Qubit, Any]: + """ + An equivalent kernel to the above, but with parallelism annotated via broadcast operations. + """ + reg = qubit.qalloc(4) + squin.broadcast.cx([reg[0], reg[2]], [reg[1], reg[3]]) + return reg + + +@squin.kernel +def conflicted_parallelized_main() -> ilist.IList[qubit.Qubit, Any]: + """ + A kernel where parallelism is annotated, but the moves cannot be done all at once due to AOD constraints. + """ + reg = qubit.qalloc(4) + squin.broadcast.cx([reg[0], reg[1]], [reg[3], reg[2]]) + return reg + + +unparallelized_main = terminal_main_wrapper( + unparallelized_main +) # hashtag METAPROGRAMMING +parallelized_main = terminal_main_wrapper(parallelized_main) +conflicted_parallelized_main = terminal_main_wrapper(conflicted_parallelized_main) + +task_unparallelized = GeminiLogicalSimulator().task(unparallelized_main) +task_parallelized = GeminiLogicalSimulator().task(parallelized_main) +task_conflicted_parallelized = GeminiLogicalSimulator().task( + conflicted_parallelized_main +) + +# %% [markdown] +# The unparallelized circuit sequentially does the two gates with two sets of moves + +# %% +_, f = task_unparallelized.fidelity_bounds() +print("Fidelity bounds for unparallelized main:", f) +task_unparallelized.tsim_circuit.diagram( + height=task_unparallelized.tsim_circuit.num_qubits * 10 +) + +# %% [markdown] +# The parallelized circuit does a parallel move and implements both gates at the same time. + +# %% +_, f = task_parallelized.fidelity_bounds() +print("Fidelity bounds for parallelized main:", f) +task_parallelized.tsim_circuit.diagram( + height=task_parallelized.tsim_circuit.num_qubits * 10 +) + +# %% [markdown] +# The parallelized but conflicted circuit implements two sequential moves and then does both gates at the same time. + +# %% +_, f = task_conflicted_parallelized.fidelity_bounds() +print("Fidelity bounds for conflicted parallelized main:", f) +task_conflicted_parallelized.tsim_circuit.diagram( + height=task_conflicted_parallelized.tsim_circuit.num_qubits * 10 +) diff --git a/mkdocs.yml b/mkdocs.yml index eec4ec6e4..bf8547e34 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -41,6 +41,8 @@ nav: - GHZ state preparation with noise: digital/examples/squin/ghz.py - TSIM examples: - Magic state distillation: digital/examples/tsim/magic_state_distillation.py + - Gemini Logical dialect examples: + - Demonstration of Simulator Device: digital/examples/gemini_logical/simulator_device_demo.py - Integration with other SDKs: - Noisy GHZ states with cirq: "digital/examples/interop/noisy_ghz.py" - Bloqade Analog: From 807e533cd79bc1188ed4089c74650c31b8424752 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 26 Mar 2026 12:01:23 -0400 Subject: [PATCH 03/10] fix: a hack to skip lanes/bytecode/arch so griffe doesn't complain --- docs/scripts/gen_ref_nav.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/scripts/gen_ref_nav.py b/docs/scripts/gen_ref_nav.py index e8fa5fde1..22280b5ec 100644 --- a/docs/scripts/gen_ref_nav.py +++ b/docs/scripts/gen_ref_nav.py @@ -48,7 +48,8 @@ "scripts/", "examples/", "crates/", - "lanes/analysis/", # TODO: don't skip this + "lanes/bytecode/arch", # TODO: don't skip this + "lanes/analysis/", "lanes/arch/gemini/", ] From 6964feea5bc8facff374c998495a2015f796b105 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 26 Mar 2026 19:09:24 -0400 Subject: [PATCH 04/10] fix: trying not to skip lanes/bytecode/arch --- docs/scripts/gen_ref_nav.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/scripts/gen_ref_nav.py b/docs/scripts/gen_ref_nav.py index 22280b5ec..5aef9864d 100644 --- a/docs/scripts/gen_ref_nav.py +++ b/docs/scripts/gen_ref_nav.py @@ -47,8 +47,7 @@ "demo/", "scripts/", "examples/", - "crates/", - "lanes/bytecode/arch", # TODO: don't skip this + "crates/", # TODO: don't skip this "lanes/analysis/", "lanes/arch/gemini/", ] From 33dfe12371425dce9048a88c4a11b8286db6e39a Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Wed, 1 Apr 2026 23:42:25 -0400 Subject: [PATCH 05/10] bumping version of bloqade-lanes --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c6e73ea9d..f651b96f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ requires-python = ">=3.10" dependencies = [ "bloqade-circuit[cirq,qasm2,qbraid,vis,stim]~=0.14.0", "bloqade-analog~=0.16.3", - "bloqade-lanes~=0.6.0", + "bloqade-lanes~=0.6.2", ] [build-system] From 2a5814a0f33aa0d361f3af6257328f8c8d45ae92 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Wed, 1 Apr 2026 23:58:44 -0400 Subject: [PATCH 06/10] bumping to new minor version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f651b96f9..f44755728 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ requires-python = ">=3.10" dependencies = [ "bloqade-circuit[cirq,qasm2,qbraid,vis,stim]~=0.14.0", "bloqade-analog~=0.16.3", - "bloqade-lanes~=0.6.2", + "bloqade-lanes~=0.7.0", ] [build-system] From 2a99fa4ec331ae4fec0884d9b114598b4e2eb811 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 2 Apr 2026 11:58:45 -0400 Subject: [PATCH 07/10] fix: downgrading pygments to try to resolve CI error --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index f44755728..0bdccbb23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,7 @@ doc = [ "mkdocs-minify-plugin>=0.8.0", "mkdocstrings[python]>=0.27.0", "mkdocs-jupyter>=0.25.1", + "pygments<2.20", ] examples = [ "networkx>=3.4.2", From 9452dccb9d8bf438dc67e868fb00d176de1cd91d Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 10 Apr 2026 13:38:22 -0400 Subject: [PATCH 08/10] feat: added a card for simulator_device_demo on docs --- .../gemini_logical/simulator_device_demo.py | 5 +++-- docs/digital/examples/index.md | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/digital/examples/gemini_logical/simulator_device_demo.py b/docs/digital/examples/gemini_logical/simulator_device_demo.py index eacdde60b..22a23b5d7 100644 --- a/docs/digital/examples/gemini_logical/simulator_device_demo.py +++ b/docs/digital/examples/gemini_logical/simulator_device_demo.py @@ -13,8 +13,9 @@ # --- # %% [markdown] -# ## Simulator demo for Gemini logical -# Gemini logical is a specific set of dialects and compilers in bloqade. This demo is hosted in the `bloqade-lanes` repo, which is where the move synthesis compiler lives. +# # Simulator Demo for Gemini Logical +# Gemini Logical is a set of dialects and compilation tools in Bloqade for logical-kernel workflows on Gemini-style architectures. +# In this tutorial, we build a simple logical kernel, inspect the generated physical and noisy circuits, and analyze detector and observable outputs using the `GeminiLogicalSimulator`. # %% # Builtins diff --git a/docs/digital/examples/index.md b/docs/digital/examples/index.md index fffb67214..d5d432e68 100644 --- a/docs/digital/examples/index.md +++ b/docs/digital/examples/index.md @@ -101,3 +101,18 @@ You can also write your quantum programs using the QASM2 dialect directly in blo Here's how to implement a Z phase gate with the repeat-until-success protocol. + + +## Gemini Logical Dialect + +Gemini Logical examples focus on logical-kernel workflows built on top of `bloqade-lanes`, including physical compilation, noisy simulation, and detector/observable analysis. + +
+ +- [Simulator Device Demonstration](../examples/gemini_logical/simulator_device_demo/) + + --- + + Compile and simulate a Gemini Logical kernel, inspect the generated circuits, and analyze detectors, observables, correction, and post-selection. + +
From f858814a0db2416d661d6f15b592a3d989541a11 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 10 Apr 2026 16:15:47 -0400 Subject: [PATCH 09/10] fix: bumped lanes ver + removed skipped docs --- docs/scripts/gen_ref_nav.py | 4 +--- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/scripts/gen_ref_nav.py b/docs/scripts/gen_ref_nav.py index 5aef9864d..eb0fd3f09 100644 --- a/docs/scripts/gen_ref_nav.py +++ b/docs/scripts/gen_ref_nav.py @@ -47,9 +47,7 @@ "demo/", "scripts/", "examples/", - "crates/", # TODO: don't skip this - "lanes/analysis/", - "lanes/arch/gemini/", + "crates/", ] diff --git a/pyproject.toml b/pyproject.toml index 0bdccbb23..566c31491 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ requires-python = ">=3.10" dependencies = [ "bloqade-circuit[cirq,qasm2,qbraid,vis,stim]~=0.14.0", "bloqade-analog~=0.16.3", - "bloqade-lanes~=0.7.0", + "bloqade-lanes~=0.7.1", ] [build-system] From b894422dce4acc454e4d5dffb24b905b33e6bcd5 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 10 Apr 2026 16:41:05 -0400 Subject: [PATCH 10/10] fix: changed hyperlink on docs to go to gemini page --- docs/reference/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/index.md b/docs/reference/index.md index af56af60e..de6128763 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -3,7 +3,7 @@ Here you can find a full API reference grouped by the respective submodules of bloqade: * [`bloqade-circuit`](./bloqade-circuit/src/bloqade/types) -* [`bloqade-lanes`](./bloqade-lanes/python/bloqade/lanes/device) +* [`bloqade-lanes`](./bloqade-lanes/python/bloqade/gemini/device) * [`bloqade-analog`](./bloqade-analog/src/bloqade/analog/) Please note that the reference is auto-generated and therefore follows the structure of the code.