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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ build.ninja
node_modules
MODULE.bazel.lock
.ninja_lock
crumble_js
bundle.js
.ipynb_checkpoints
3 changes: 2 additions & 1 deletion glue/crumble/draw/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ const pitch = 50;
const rad = 10;
const OFFSET_X = -pitch + Math.floor(pitch / 4) + 0.5;
const OFFSET_Y = -pitch + Math.floor(pitch / 4) + 0.5;
let curveConnectors = true;

export {pitch, rad, OFFSET_X, OFFSET_Y};
export {pitch, rad, OFFSET_X, OFFSET_Y, curveConnectors};
4 changes: 2 additions & 2 deletions glue/crumble/gates/gate_draw_util.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {pitch, rad} from "../draw/config.js"
import { curveConnectors, pitch, rad } from "../draw/config.js";

/**
* @param {!CanvasRenderingContext2D} ctx
Expand Down Expand Up @@ -210,7 +210,7 @@ function stroke_connector_to(ctx, x1, y1, x2, y2) {

ctx.beginPath();
ctx.moveTo(x1, y1);
if (d < pitch * 1.1) {
if (!curveConnectors || d < pitch * 1.1) {
ctx.lineTo(x2, y2);
} else {
ctx.bezierCurveTo(x1 + ux + px, y1 + uy + py, x2 - ux + px, y2 - uy + py, x2, y2);
Expand Down
190 changes: 190 additions & 0 deletions glue/crumpy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<div align="center">

# CrumPy

Visualize quantum circuits with error propagation in a Jupyter widget.

<img src="https://i.imgur.com/LVu7H1l.png" alt="jupyter notebook cell with quantum circuit diagram output" width=400>

---

This package builds off of the existing circuit visualizations of
[Crumble](https://algassert.com/crumble), a stabilizer circuit editor and
sub-project of the open-source stabilizer circuit project
[Stim](https://github.com/quantumlib/Stim).

</div>

---

## Installation

**Requires:** Python 3.11+

```console
pip install crumpy
```

## Usage

CrumPy provides a convenient Jupyter widget that makes use of Crumble's ability
to generate quantum circuit timeline visualizations with Pauli propagation from
[Stim circuit specifications](https://github.com/quantumlib/Stim/blob/main/doc/file_format_stim_circuit.md#the-stim-circuit-file-format-stim).

### Using `CircuitWidget`

`CircuitWidget` takes a
[Stim circuit specification](https://github.com/quantumlib/Stim/blob/main/doc/file_format_stim_circuit.md#the-stim-circuit-file-format-stim)
in the form of a Python `str`. To convert from the official Stim package's
`stim.Circuit`, use `str(stim_circuit)`. If coming from another circuit type, it
is recommended to first convert to a `stim.Circuit` (e.g., with Stim's
[`stimcirq`](https://github.com/quantumlib/Stim/tree/main/glue/cirq) package),
then to `str`. Note that not all circuits may be convertible to Stim.

```python
from crumpy import CircuitWidget

your_circuit = """
H 0
CNOT 0 1
"""

circuit = CircuitWidget(stim=your_circuit)
circuit
```

<img src="https://i.imgur.com/AOM0soZ.png" alt="quantum circuit that creates EPR pair" width=175>

### Propagating Paulis

A useful feature of Crumble (and CrumPy) is the ability to propagate Paulis
through a quantum circuit. Propagation is done automatically based on the
specified circuit and Pauli markers. From the
[Crumble docs](https://github.com/quantumlib/Stim/tree/main/glue/crumble#readme):

> Propagating Paulis is done by placing markers to indicate where to add terms.
> Each marker has a type (X, Y, or Z) and an index (0-9) indicating which
> indexed Pauli product the marker is modifying.

Define a Pauli X, Y, or Z marker with the `#!pragma MARK_` instruction. Note
that for compatibility with Stim, the '`#!pragma `' is included as `MARK_` is
not considered a standard Stim instruction:

```python
z_error_on_qubit_2 = "#!pragma MARKZ(0) 2"
```

#### Legend

<img src="https://i.imgur.com/Zt0G5k8.png" alt="red Pauli X, green Pauli Y, blue Pauli Z markers" width=200>

#### Example

```python
circuit_with_error = """
#!pragma MARKX(0) 0
TICK
CNOT 0 1
TICK
H 0
"""

CircuitWidget(stim=circuit_with_error)
```

<img src="https://i.imgur.com/5CH1OFQ.png" alt="quantum circuit with Pauli propagation markers" width=200>

Notice how the single specified Pauli X marker propagates both through the
control and across the connector of the CNOT, and gets transformed into a Pauli
Z error when it encounters an H gate.

## Local Development

See the [contribution guidelines](.github/CONTRIBUTING.md) for a quick start
guide and Python best practices.

### Additional requirements

[npm/Node.js](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
(for bundling JavaScript); versions 11+/22+ recommended

### Project Layout

CrumPy makes use of the widget creation tool
[AnyWidget](https://anywidget.dev/). With AnyWidget, Python classes are able to
take JavaScript and display custom widgets in Jupyter environments. In order to
visualize Crumble within CrumPy, some modifications in the Crumble source code
were needed. CrumPy therefore includes both JavaScript and Python subparts:

```text
glue/
├── crumpy/ # Modified Crumble code
│ ├── src/
│ │ ├── crumpy/
│ │ │ └── __init__.py # Python code for CircuitWidget
│ │ ├── js/
│ │ │ ├── draw/ # Modified JS source code
│ │ │ ├── main.js # Main circuit visualization/setup
│ │ │ ├── bundle.js # Bundled JavaScript will appear here
│ │ │ └── package.json # Bundling setup and scripts
│ └── ...
│ ├── tests/ # Python tests
│ ├── simple_example.ipynb # Example notebook
│ └── getting_started.ipynb # Getting started notebook
```

`glue/crumpy` contains the main Python package code.

`glue/crumpy/src/crumpy/__init__.py` contains the main class of the `crumpy` package,
`CircuitWidget`.

`glue/crumpy/src/js/` contains the modified Crumble circuit visualization code that
will be rendered in the `CircuitWidget` widget. In the build step, we copy the Crumble
source code and modify it with the `.js` files in this folder.

`glue/crumpy/src/js/main.js` contains the main circuit visualization and setup logic
in the form of the `render` function
[used by AnyWidget](https://anywidget.dev/en/afm/#anywidget-front-end-module-afm).

### Bundling

To create a Jupyter widget, AnyWidget takes in JavaScript as an ECMAScript
Module (ESM). **Any changes made to the JavaScript code that backs the circuit
visualization will require re-bundling** into one optimized ESM file.

To bundle the JavaScript:

1. Navigate to `glue/crumpy/src/js`
2. If you haven't yet, run `npm install` (this will install the
[esbuild](https://esbuild.github.io/) bundler)
3. Run `npm run build` (or `npm run watch` for watch mode)

A new bundle should appear at `src/js/bundle.js`.

**Note**: If you are working in a Jupyter notebook and re-bundle the JavaScript,
you may need to restart the notebook kernel and rerun any widget-displaying code
for the changes to take effect.

### Install from source

After creating the bundle, you can install the package from source:

1. Navigate to `glue/crumpy`
2. Run `pip install -e .` in your favorite Python environment

<!-- SPHINX-START -->

<!-- prettier-ignore-start -->
[pypi-link]: https://pypi.org/project/crumpy/
[pypi-platforms]: https://img.shields.io/pypi/pyversions/crumpy
[pypi-version]: https://img.shields.io/pypi/v/crumpy

<!-- prettier-ignore-end -->

## Attribution

This package was created as part of [Sam Zappa](https://github.com/zzzappy)'s
internship at [Riverlane](https://github.com/riverlane) during the Summer
of 2025. Thanks to [Hyeok Kim](https://github.com/see-mike-out),
[Leilani Battle](https://github.com/leibatt), [Abe Asfaw](https://github.com/aasfaw)
and [Guen Prawiroatmodjo](https://github.com/guenp) for guidance and useful discussions.
Loading
Loading