← All writing

What is ZX calculus, and why should compiler writers care?

A ground-up introduction to the ZX graphical calculus — why it is a natural intermediate representation for quantum circuits, and how its rewrite rules expose optimization opportunities invisible to gate-level passes.

Every optimizing compiler needs an intermediate representation that is close enough to the hardware to reason about costs, yet abstract enough to expose structure that local gate rewrites cannot see. For classical circuits, this is usually an SSA-form control-flow graph. For quantum circuits, the standard choice has been a flat sequence of gates — and it is a poor choice. ZX calculus is a better one.

The problem with gate-level representations

When you write a quantum circuit as a list of gates, you inherit all the accidents of whichever gate set you started with. Two circuits that compute identical unitaries look completely different on paper. A compiler pass that simplifies $CX \cdot CX$ into the identity only fires when those two gates are adjacent and in the right order — it cannot see the same opportunity buried under a layer of single-qubit rotations.

This is not a tooling problem. It is a representation problem. A gate list encodes how a computation proceeds, not what it computes. Optimization means finding different hows that produce the same what, so you want a representation that makes the what primary.

The canonical solution in classical compilation is to lift into a semantics-preserving intermediate form — lambda calculus, CPS, or an effect system — where the identity of computations becomes transparent. ZX calculus does the same thing for quantum circuits: it lifts gate sequences into a graphical language where the semantics are carried by the graph topology and edge labels, not by the order in which gates were written down.

What ZX calculus actually is

A ZX diagram is a labeled open graph. The nodes are one of two types:

  • Z-spiders (circles): represent rotations around the Z axis of the Bloch sphere. A Z-spider with phase $\alpha$ computes a specific multilinear map between input and output qubits. With phase 0 it acts as a wire merger/splitter.
  • X-spiders (diamonds or rotated squares): represent rotations around the X axis. Same structure, different Pauli basis.

Wires connect spiders. Dangling edges on the left are inputs; dangling edges on the right are outputs. The entire network denotes a linear map from $(\mathbb{C}^2)^{\otimes m}$ to $(\mathbb{C}^2)^{\otimes n}$.

What makes this useful is that the same linear map can be represented by infinitely many different diagrams, and the rewrite rules that connect them are both local and complete. The core rules you need in practice are:

  • Spider fusion: two same-color spiders connected by a wire fuse into one, with phases added.
  • Identity removal: a spider with phase 0 and exactly two wires is a plain wire — remove it.
  • Color change (Hadamard): a Hadamard gate converts a Z-spider to an X-spider and vice versa. This is represented by a yellow box on a wire (or by a dashed wire convention).
  • Bialgebra rule: a Z-spider connected to an X-spider in a specific pattern rewrites to a different crossing pattern. This is the non-trivial rule that does real work.
  • $\pi$-copy: a Z-spider with phase $\pi$ can be pushed through an X-spider, flipping the X-spider's phase.

These rules are sound — they preserve the semantics of the diagram — and together they are complete for the stabilizer fragment: any two diagrams that represent the same stabilizer operation can be transformed into each other using only these rules.

Why this matters for optimization

The completeness result is the key insight. For stabilizer circuits (Clifford circuits), ZX calculus is a decision procedure: you can normalize any diagram to a canonical form and read off the circuit directly. T-count optimization — reducing the number of non-Clifford T gates, which are expensive in fault-tolerant architectures — becomes a graph rewriting problem. You apply the spider fusion and $\pi$-copy rules to collapse as many T gates as possible before they interact with each other.

PyZX, the reference implementation, uses exactly this approach and achieves T-count reductions competitive with the best known classical methods, often in milliseconds, on circuits where gate-level peephole optimization does nothing useful.

For a compiler writer, the implications are:

  1. Circuit ingestion: parse the gate sequence into a ZX diagram once. From this point on, you are working with a graph, not a list.
  2. Optimization passes: apply rewrite rules. Each rule is a local graph transformation — no global analysis required. Rules commute freely because the semantics are in the graph, not the order.
  3. Extraction: convert the optimized diagram back to a gate sequence. This is the hard direction — not all ZX diagrams are directly extractable, and extraction algorithms (like the one in PyZX based on graph-theoretic flow conditions) are an active research area.
  4. Error analysis: Pauli errors in stabilizer circuits propagate through the ZX graph according to the same rewrite rules. A compiler pass can trace symbolic error terms alongside the circuit transformation, producing error bounds as a side product of optimization.

Practical implications for compiler writers

If you are building a quantum compiler today, the decision about whether to use ZX calculus as your IR is really a decision about which passes you want to write.

Gate-level passes are easy to write and reason about locally. If your target is NISQ hardware with shallow circuits and you mostly need routing and native-gate decomposition, a gate list with some peephole rewrites is probably enough.

If your target is fault-tolerant hardware — where T-gate count directly determines runtime cost, where error budgets need to be tracked statically, and where circuits are deep enough that local passes leave significant optimization on the table — ZX calculus is the right choice. The overhead of the ingestion/extraction round-trip pays for itself quickly.

The other consideration is extensibility. ZX calculus has a clean extension to mixed-state circuits (ZX with classical control), to qudits (higher-dimensional spiders), and to measurement-based quantum computation. If you expect your compiler to grow in these directions, a ZX-based IR is more future-proof than a gate list extended with ad hoc annotations.

Where to go from here

The canonical reference is Coecke and Kissinger's Picturing Quantum Processes — it is a full textbook treatment and does not assume prior quantum background. For the T-count optimization story specifically, the PyZX paper (Kissinger & van de Wetering, 2020) is the best starting point. For the connection to fault-tolerant compilation and Pauli frame tracking, the ZX-based approach to surface code compilation developed by Bombin and collaborators shows how far the graphical language can be pushed toward physical hardware.

The short version: if you care about what a quantum circuit computes rather than just how it is written, ZX calculus gives you the representation where that question has tractable, complete answers.