qrab 0.1.3

A readable quantum-circuit compiler for TikZ, Typst, SVG, and Quirk
Documentation

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]
}

The bell pair circuit rendered by qrab

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, named style blocks, and relative imports, all checked at compile time.
  • Structured layout. parallel and overlay blocks, repeat, and reverse replay 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.
  • 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 { … } and backend 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:

cargo install --path . --locked

Usage

qrab check circuit.qrab                      # parse and report, render nothing
qrab compile circuit.qrab                    # also writes circuit.url
qrab compile circuit.qrab --target svg       # writes circuit.svg
qrab compile circuit.qrab --target quirk     # writes an interactive URL to circuit.url
qrab compile circuit.qrab -t latex -o out.tex

--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 circuit.tex --outdir build
typst compile circuit.typ build/circuit.pdf

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.

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.

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
}

The teleportation circuit

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
}

An annotated circuit with braces, a note, and a cut

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]
}

The GHZ preparation circuit

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; escapes.qrab reaches into one backend; and imports.qrab pulls declarations from another file.

Documentation

The qrab Manual is the place to start: 42 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. Parity with qpic and its evidence are tracked in docs/qpic-coverage.md; building, testing, and adding an example are covered by CONTRIBUTING.md, and maintainers can follow docs/releasing.md.

License

MIT. See LICENSE.