Getting Started with CrumPy¶
To start, install the crumpy package:
[1]:
%pip install crumpy
/home/runner/work/crumpy/crumpy/.nox/docs/bin/python3: No module named pip
Note: you may need to restart the kernel to use updated packages.
Required Imports¶
[2]:
from crumpy import CircuitWidget
import stim
import ipywidgets
import cirq
import qiskit
Background: Crumble¶
The goal of CrumPy’s CircuitWidget is to utilize the visualization features of Crumble (website, docs), a stabilizer circuit editor and sub-project of the open-source stabilizer circuit project Stim, to provide a convenient way to display quantum circuit timelines with Pauli propagation in a Jupyter notebook. In particular, CrumPy takes
advantage of Crumble’s existing ability to generate circuit timeline visualizations from Stim circuit specifications, which also includes support for Pauli propagation markers.
If you aren’t familiar with Crumble, try experimenting with building a circuit from scratch, adding Paulis, or check out some of Crumble’s example circuits.
The logic behind Crumble’s Stim import textbox (shown on click of the Show Import/Export button in Crumble) provides the basis of CrumPy’s circuit importing abilities. Try playing around with importing/exporting circuits to get familiar with the notation and instructions supported by Crumble and CrumPy.
Crumble ↔ CrumPy¶
CrumPy uses the circuit compilation logic from Crumble, ensuring that circuits in Crumble and CrumPy are compatible with one another. Note that this means CrumPy supports Crumble-specific instructions not present in standard Stim circuit specification, like MARKX and POLYGON. This makes the transfer of a Crumble circuit to CrumPy and vice versa fairly straightforward:
If you’ve already built a circuit in Crumble that you wish to display using CrumPy:
(in Crumble) Click Show Import/Export → Export to Stim Circuit
Copy the Stim output from the textarea
(in Python/notebook) Paste the output into the content of a Python string
Use your circuit string with
CircuitWidget!
Going from CrumPy to Crumble:
(in Python/notebook) Copy your circuit string (tip: try copying the output of
print(your_circuit.stim))(in Crumble) Click Show Import/Export
Paste your circuit into the textarea
Click Import from Stim Circuit to see your circuit!
Using CircuitWidget¶
At the core of crumpy is the CircuitWidget. To get started with CircuitWidget, import it from the crumpy package:
[3]:
from crumpy import CircuitWidget
Basic Usage¶
CircuitWidget takes a Stim circuit specification in the form of a Python string:
[4]:
stim_circuit = """
H 0
CNOT 0 2
SWAP 0 1
M 1
"""
widg = CircuitWidget(stim=stim_circuit)
widg
[4]:
It’s also possible to update the circuit of an existing CircuitWidget. Running the cell below will update the output of the previous cell to reflect the new circuit:
[5]:
new_circuit = """
CX 0 2
TICK
CY 1 2
TICK
CZ 0 1
"""
widg.stim = new_circuit
CircuitWidget also has configuration options that change the appearance of the circuit. Notice again how the above output is updated:
[6]:
widg.curveConnectors = False
widg.indentCircuitLines = False
# Can also specify within CircuitWidget constructor, e.g.:
# CircuitWidget(stim=..., curveConnectors=False)
Propagating Paulis¶
A useful feature of Crumble (and CrumPy) is the ability to propagate Paulis through a quantum circuit. From the Crumble docs:
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:
[7]:
x_error_on_qubit_3 = "#!pragma MARKX(0) 3"
z_error_on_qubit_2 = "MARKZ(0) 2" # Valid for use with CrumPy, but not with stim.Circuit - see below

[8]:
circuit_with_mark = """
#!pragma MARKX(0) 0
TICK
CNOT 0 1
TICK
H 0
"""
CircuitWidget(stim=circuit_with_mark)
[8]:
Notice how the red Pauli X marker defined on qubit 0 propagates through the CNOT, resulting in X markers on both qubits. Then, when the X marker on qubit 0 encounters the H gate, it is converted into a blue Pauli Z marker. This propagation is done automatically based on the provided circuit and MARKX.
Some instructions used in Crumble (and therefore CrumPy) are not considered standard Stim notation. This includes MARK_, POLYGON, and ERR instructions.
For example, consider the circuit specification below which omits the ‘#!pragma’ preceding its MARKZ instruction. Attempting to create a stim.Circuit will cause an error:
[9]:
no_pragma = """
MARKZ(0) 0
Y 1
TICK
CZ 0 1
"""
stim.Circuit(no_pragma)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[9], line 8
1 no_pragma = """
2 MARKZ(0) 0
3 Y 1
4 TICK
5 CZ 0 1
6 """
----> 8 stim.Circuit(no_pragma)
ValueError: Gate not found: 'MARKZ'
Including the ‘#!pragma’ avoids this issue. The MARKZ instruction will not show up in a stim.Circuit (the ‘#’ in ‘#!pragma’ comments out the line):
[10]:
with_pragma = """
#!pragma MARKZ(0) 0
Y 1
TICK
CZ 0 1
"""
ok_circuit = stim.Circuit(with_pragma)
ok_circuit
[10]:
stim.Circuit('''
Y 1
TICK
CZ 0 1
''')
Even though there is no MARKZ, we now have a valid stim.Circuit which can easily be used with CircuitWidget:
[11]:
CircuitWidget(stim=str(ok_circuit))
[11]:
Composing with Other Widgets¶
The ability to programmatically update a CircuitWidget’s stim attribute (also its config options) provides an opportunity for more advanced interaction with the circuit visualization. In particular, we can utilize existing widgets from the ipywidgets package to create more interactive experiences.
The traitlets package used behind the scenes of both CircuitWidget and the widgets of ipywidgets allows for the composition of multiple widgets through functions like link and dlink. See the ipywidgets documentation for more advanced uses.
In this example, an IntSlider widget is used in conjunction with a CircuitWidget to update the displayed circuit based on the slider’s value. The ipywidget.dlink function is used to directionally link the slider’s value to the circuit’s stim (i.e. updates to the slider’s value will update the circuit’s stim). Here we can use the slider to see how a Pauli X marker acts when faced with consecutive H gates:
[12]:
def sliderToCircuit(slider_val):
base_circuit = """
#!pragma MARKX(0) 0
TICK
CX 0 1
"""
return base_circuit + "H 0\n" * slider_val
circuit = CircuitWidget()
slider = ipywidgets.IntSlider(description="# of H gates", value=1, min=0, max=10)
ipywidgets.dlink((slider, "value"), (circuit, "stim"), sliderToCircuit)
display(slider, circuit)
Converting Directly from a Circuit (stim, cirq, qiskit)¶
CrumPy provides helper methods for creating a CircuitWidget directly from 3 popular quantum circuit packages: stim, cirq, and qiskit.
Even if stim isn’t your quantum circuit package of choice, it may still be possible to utilize CrumPy. Circuits from packages like cirq or qiskit may be convertible to a stim circuit. For circuit languages that don’t have an existing transpiler that converts directly to Stim, it may also be possible to convert to OpenQASM, which can be converted to Cirq and
finally converted to Stim.
Note that it may not always be possible to convert a circuit between formats (notably, stim has a limited gate set).
CircuitWidget.from_stim
[13]:
stim_circuit = stim.Circuit("""
H 0
CX 0 1
M 0 1
""")
CircuitWidget.from_stim(stim_circuit)
[13]:
CircuitWidget.from_cirq
[14]:
q0 = cirq.LineQubit(0)
q1 = cirq.LineQubit(1)
cirq_circuit = cirq.Circuit(cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1))
CircuitWidget.from_cirq(cirq_circuit)
[14]:
CircuitWidget.from_qiskit
[15]:
qiskit_circuit = qiskit.QuantumCircuit(2, 2)
qiskit_circuit.h(0)
qiskit_circuit.cx(0, 1)
qiskit_circuit.measure([0, 1], [0, 1])
CircuitWidget.from_qiskit(qiskit_circuit)
[15]:
While it’s not possible to directly add Pauli markers or other Crumble-specific instructions when creating CircuitWidgets with these methods, it is possible to read/write the resulting CircuitWidget’s .stim attribute to get the final converted Stim circuit (in string form), which can then be modified.