qrab
qrab compiles a small, readable quantum-circuit language into standalone
LaTeX/TikZ, Typst (Quill), SVG, or a URL for
the interactive Quirk simulator. It is inspired
by qpic, but a circuit is written as statements
in a language with types, functions, and imports rather than as positional
commands and token-substitution macros.
circuit bell_pair {
qubit q[2]: "|0>" -> "bell"
h q[0]
x q[1] if q[0]
measure q[0], q[1]
}
Try it in the browser: playground — the compiler runs as WebAssembly, so nothing is uploaded and nothing is installed.
Features
- Four backends from one source. LaTeX/TikZ and Typst/Quill for papers, SVG for the web and quick previews, and Quirk URLs for interactive simulation. SVG and Quirk need no external toolchain.
- Named wires, arrays, and ranges.
qubit q[4],q[0..3], and named registers instead of counting rows. - Real composition.
fn,let, namedstyleblocks, and relativeimports, all checked at compile time. - Structured layout.
parallelandoverlayblocks,repeat, andreversereplay of a marked range. - The full qpic visual model. Controls, boxes, measurements, swaps, barriers, braces, cuts, notes, groups, permutations, ellipses, wire lifecycle changes, and orientation.
- Portable styling. Twelve named colors plus hex, sizes in points, shapes, opacity, and hyperlinks — all of which mean the same thing in every document backend.
- Portable mathematical text. Every visible string accepts literal text
with inline
$...$LaTeX math, rendered consistently in LaTeX, Typst, and SVG. - Diagnostics that point at your source. Errors carry a line, a column, a labelled span, and a suggestion, and one run reports more than one error.
- Escape hatches.
backend latex { … }andbackend typst { … }inject raw code into exactly one backend.
Design philosophy
A circuit is a program, not a picture. Positional macro languages make the
easy diagram quick and the interesting diagram unmaintainable. qrab gives you
the constructs you already reach for — naming a thing, calling it twice,
importing it from another file — and checks that you used them correctly.
Say it once, get every format. The parser produces one semantic model. Each backend renders that model; none of them can see the source text.
Portable styling or none at all. Style options exist only where all
document backends can honor them. Anything genuinely backend-specific goes in
a backend block, where it is visibly quarantined instead of silently ignored
somewhere else. Quirk intentionally receives circuit semantics, not page
styling.
Errors are part of the language. Every diagnostic knows where it came
from — across import boundaries — and says what to do about it.
Install
The distribution channels below are configured but stay pending until the first public release.
| Channel | Command |
|---|---|
| Cargo | cargo install qrab |
| cargo-binstall | cargo binstall qrab |
| npm / Bun | npm i -g @inmzhang/qrab · npx @inmzhang/qrab · bunx @inmzhang/qrab |
| Homebrew | brew install --formula https://github.com/inmzhang/qrab/releases/latest/download/qrab.rb |
| Shell | curl --proto '=https' --tlsv1.2 -LsSf https://github.com/inmzhang/qrab/releases/latest/download/qrab-installer.sh | sh |
| PowerShell | powershell -ExecutionPolicy Bypass -c "irm https://github.com/inmzhang/qrab/releases/latest/download/qrab-installer.ps1 | iex" |
Until then, install from a checkout:
Usage
--target takes latex (alias tikz), typst (alias quill), svg,
quirk, or all; -o requires a single backend. SVG and the Quirk URL are
finished output. The other two are sources, so hand them to their own compiler:
Tectonic and Typst are only needed to turn those sources into PDFs; neither is
required for qrab check, for source generation, or for SVG. Typst 0.15.1
downloads Quill 0.8.0 on its first build and MiTeX 0.2.7 when a diagram uses
mathematical text.
All visible strings use the same convention: text outside $...$ is literal,
and text inside is LaTeX math. This applies to wire endpoints, gates,
measurements, labels, notes, braces, cuts, bundles, and groups; write \$
inside a quoted qrab string for a literal dollar sign. The LaTeX backend emits
the math directly, Typst uses MiTeX, and SVG renders it as paths.
The Quirk target maps H, X, Y, Z, S, T, indexed phase gates, controls,
measurements, swaps, and recognized input states. Arbitrary named boxes become
labeled no-op custom gates because qrab does not define their unitary. Quirk is
limited to 16 qubits; regions, styling, wire labels, and other drawing-only
annotations without a Quirk equivalent are omitted.
Quirk has no math typesetter, so it removes the $ delimiters and uses the
remaining TeX source as the closest deterministic label.
Examples
Every example below is a file in examples/, and every diagram is
generated from it by qrab itself. The three run from the simplest circuit to
the most involved.
Teleportation
Named wires carry their input and output labels. if turns any gate into a
controlled gate, measure switches a wire to a classical double rule, and
barrier separates the protocol's phases.
circuit teleportation {
qubit message: "|psi>" -> "|psi>"
qubit alice: "|0>"
qubit bob: "|0>" -> "|psi>"
h alice
x bob if alice
barrier
x alice if message
h message
measure message, alice
x bob if alice
z bob if message
}
Annotations
Ranges (q[0..3]) select several wires at once. labels, brace, equals,
note, and cut annotate a column without consuming one, and layout tunes
the shared geometry that every document backend reads.
circuit annotations {
layout {
gate_size: 22
corner_radius: 3
comment_width: 72
}
qubit q[3]: "|0>" -> "out"
labels "data", "work", "flag" on q[0..3] with fill: yellow
brace left "input" on q[0..3] with stroke: blue
h q[0]
note "decode" on q[1]
equals "encode" on q[0..3] braced both with fill: "#F3F3F3", stroke: teal
brace right "result" on q[0..3] with stroke: purple
cut on q[0..3] as "stage" with stroke: red
x q[2] if q[0]
brace both "repeat" on q[0..3] with stroke: blue
}
Functions, constants, and styles
let names a value, style names a set of portable style options, and fn
names a sequence of operations over wire parameters. Functions call functions,
and arity is checked at the call site. This is the whole point of the language:
the circuit below has one description of "entangle", used three times.
let entangler = "E"
let accent = "#DDEEFF"
style highlighted {
fill: accent
stroke: blue
shape: circle
}
fn entangle(control, target) {
h control
gate entangler on target if control with highlighted
}
fn prepare_ghz(first, second, third) {
entangle(first, second)
x third if second
barrier
}
circuit function_composition {
qubit q[3]: "|0>" -> "GHZ"
prepare_ghz(q[0], q[1], q[2])
gate entangler on q[0], q[2] with highlighted, opacity: 0.7
measure q[0], q[1], q[2]
}
More
lifecycle.qrab bounds a wire's lifetime with
start and end, changes the kind of line it draws, and reorders rows with
permute; styling.qrab draws the same circuit
top-to-bottom on a tinted background with per-gate colors and shapes;
regions.qrab boxes a marked range of operations;
programming.qrab uses repeat and reverse;
measurements.qrab covers the measurement
shapes; ellipsis.qrab omits register rows;
math-labels.qrab uses inline LaTeX math
throughout one diagram;
escapes.qrab reaches into one backend; and
imports.qrab pulls declarations from another file.
Documentation
The qrab Manual is the place to start: 43 pages covering
the language, the compiler, and the rendering model, with a worked, rendered
example for every construct. It is typeset from
docs/manual.typ, and every snippet in it is a real file under
docs/manual/examples that qrab itself renders, so the
code and the picture beside it can never drift apart.
The library API is documented on docs.rs. See the qpic compatibility notes, contributor guide, and release guide for maintenance details.
License
MIT. See LICENSE.