DEV Community

kristian koci
kristian koci

Posted on

I built three tools for Quantinuum's guppy stack. Along the way I found six real bugs.

I built three tools for Quantinuum's guppy/HUGR stack. Along the way I found six real bugs.

I've been working with guppylang — Quantinuum's
Python-embedded quantum programming language, compiling to HUGR, running on their Selene
simulator and trapped-ion hardware. It's a young ecosystem, and I wanted to build things that
were actually useful, not just demos. That meant treating correctness as the whole point, not
an afterthought — every formula cited to its source, every claim about compiler behavior
verified by actually compiling code and inspecting the output, not assumed from docs.

That discipline turned out to matter more than expected. Building three fairly ordinary
developer tools surfaced six real, confirmed bugs — four in guppylang itself, two in Google's
Qualtran (a widely-used quantum resource-estimation library) — none of which I was looking for
going in.

The three tools

qshelf — a tested package registry of quantum algorithm
implementations for guppylang/HUGR (QFT, Grover, QAOA, VQE-H2), each verified against an
independent mathematical reference (exact linear algebra, scipy.linalg.expm, exact
diagonalization), not just "it ran without crashing."

Estimand — a fault-tolerant resource estimator for
guppy/HUGR programs. Given a compiled guppy circuit, it estimates physical qubit count,
runtime, and error probability under a surface-code scheme. It's an adapter, not a
resource-estimation engine — it extracts a gate-count summary from real guppy control flow
(conditionals, nested loops, cross-function calls, even CallIndirect) and feeds it to
Qualtran's already-published cost models. Verified end-to-end against unmodified QFT and
Grover implementations.

qmatchpoint — wires PyMatching (an established,
peer-reviewed decoder) to the syndrome bits a guppy QEC circuit produces, since nothing in the
guppylang/HUGR/Selene stack currently does decoding.

The bugs

Building qshelf against real algorithm math found four guppylang issues: a wrong unitary from
iqft compiled standalone vs. combined with qft, a wrong unitary from multi-controlled Z
(later fixed upstream), a rejected generic array-length type, and a rejected numpy.ndarray
closure (reclassified as a feature request).

The more interesting ones came from Estimand. Its whole job is turning guppy programs into gate
counts, then trusting Qualtran's surface-code math to do the rest — so I went and checked that
math against the actual cited papers (Beverland et al. 2022, Litinski 2019 x2), rather than
trusting the citation. Two real discrepancies turned up:

  • CompactDataBlock's tile-count formula was missing an additive constant from its own cited paper (arxiv.org/abs/1808.02892, Fig. 9) — confirmed by a maintainer, and by the time I got around to fixing it, main had already drifted to a different wrong version of the same formula. PR here, awaiting review.
  • make_beverland_et_al()'s magic-state factory error model silently used Beverland's threshold constant instead of Litinski's own, inside a component that's otherwise a faithful reimplementation of Litinski's paper. Turned into a longer, still-open discussion about whether the whole preset should more faithfully reproduce Beverland's actual architecture choices (data block, factory grid-search) rather than borrowing Litinski's defaults.

None of this was the goal going in. It's just what happens when "does this actually match what
it claims to implement" is a mandatory question, not an optional one, and I think that's a more
useful takeaway than any of the three tools individually — verification discipline finds real
things, even (especially) in mature, widely-used libraries.

Repos linked above if any of it's useful, and happy to talk through any of the decisions behind
them.

Top comments (0)