Skip to content

Quantum Fourier transform

Reference: the Quon constructs in this recipe are defined normatively — syntax, typing contract, constraints, and a minimal example — in the Language reference.

The Quantum Fourier Transform (QFT) is the quantum analogue of the discrete Fourier transform: it maps a computational-basis state |x⟩ to a superposition whose phases encode the frequency content of x. It is a subroutine in Shor’s algorithm, quantum phase estimation, and many hidden-subgroup problems. Unlike the classical FFT which runs in O(n log n) for n bits, the QFT runs in O(n²) gates — but it produces a quantum state, not classical data.

The QFT has a beautiful recursive structure: QFT(n) consists of a Hadamard on the first qubit, a sequence of controlled-R_z rotations with geometrically decreasing angles (π/2, π/4, π/8, …), and then QFT(n-1) on the remaining n-1 qubits, embedded into the high qubits via on_high. A final bit-reversal permutation (swap_reverse) restores conventional output ordering. This recursion is the reason QFT is Quon’s headline example for value-dependent types: the circuit type Circuit<n, n, ...> depends on the Nat parameter n, and the recursion match n { 0 => identity(0), _ => ... qft(n-1) ... } is elaborated by partial evaluation.

The verification strategy is a round-trip test: apply qft(n) |> adjoint(qft(n)) and check that any computational-basis input is returned unchanged. A measurement histogram cannot directly observe QFT’s output phases, so this is the documented deviation from the original “matches theoretical DFT” acceptance criterion — the round trip is the only checkable proxy. It verifies that the QFT and its adjoint are exact inverses — which exercises the same recursive, on_high, swap_reverse, and controlled-rotation machinery as qft alone. If the round trip returns the input with probability 1.0, the recursive structure is correct. In practice the optimizer proves this structurally: gate_cancellation and rotation_merging, running to fixpoint, cancel the entire qft |> adjoint(qft) sequence down to nothing.

fn apply_hadamard(n: Nat): Circuit<n, n, 1, Clifford> = circuit { H @0 }
fn controlled_rotations(n: Nat): Circuit<n, n, 2 * (n - 1), Universal> = circuit {
for i in range(n - 1) { controlled(Rz(PI / (2.0 ^ (i + 1)))) @(0, i + 1) }
}
fn qft(n: Nat): Circuit<n, n, 2 * n * n, Universal> =
match n {
0 => identity(0),
_ => apply_hadamard(n)
|> controlled_rotations(n)
|> (qft(n - 1) `on_high` n)
|> swap_reverse(n)
}
fn qft_roundtrip(n: Nat): Circuit<n, n, 4 * n * n, Universal> = circuit {
qft(n) |> adjoint(qft(n))
}

The types here are genuinely value-dependent — the depth bound contains arithmetic expressions in n:

  • apply_hadamard is Circuit<n, n, 1, Clifford> — a single H on qubit 0. Depth 1 regardless of n because only one gate is applied.
  • controlled_rotations is Circuit<n, n, 2 * (n - 1), Universal> — the depth is 2(n-1) because the for loop runs n-1 times, each iteration applying one controlled rotation (depth 2: the control and target are on different qubits, but controlled rotations count as depth 2 in the typechecker’s convention). The class is Universal because Rz is not Clifford — it is a general rotation.
  • qft is Circuit<n, n, 2 * n * n, Universal> — the depth bound is 2n², which the typechecker proves by induction: the base case qft(0) is identity(0) with depth 0, and the recursive case sums the depths of apply_hadamard (1), controlled_rotations (2(n-1)), qft(n-1) on_high n (embedded, so 2(n-1)²), and swap_reverse (a permutation, constant depth). The typechecker verifies that the sum fits within 2n².
  • qft_roundtrip is Circuit<n, n, 4 * n * n, Universal> — depth 4n², exactly twice the QFT depth, because adjoint(qft(n)) has the same depth as qft(n) and they are composed sequentially.

The Universal class reflects the controlled rotations: Rz(θ) is not a Clifford gate for general θ, so the QFT as a whole is classified as Universal. The subtyping Clifford ⊑ Universal means the apply_hadamard subcircuit (which is Clifford) composes into the Universal QFT without issue.

The page embeds test/verify/qft.qn. Unlike the typechecker stress fixture frontend/tests/fixtures/corpus/recursive_qft.qn, this program uses geometrically decreasing controlled-rotation angles — the fixture’s angle is genuinely PI / 2^(i+1) as a physical QFT requires, not the PI/4 constant the typechecker-only stress test fixes purely to exercise the value-dependent machinery.

The recursive QFT is built from three helpers. apply_hadamard puts a Hadamard on qubit 0 — the “first” qubit of the register. controlled_rotations then applies a sequence of controlled Rz rotations from qubit 0 to each higher qubit, with the rotation angle decreasing geometrically: the i-th rotation has angle π/2^(i+1). This is the defining structure of the QFT — the geometrically decreasing angles produce the interference pattern that encodes the frequency content. The qft function itself is recursive: the base case qft(0) is the identity on zero qubits, and the recursive case composes the Hadamard, the controlled rotations, a smaller qft(n-1) embedded into the high qubits via on_high, and a swap_reverse permutation to fix the bit-reversed output ordering.

fn apply_hadamard(n: Nat): Circuit<n, n, 1, Clifford> = circuit { H @0 }
fn controlled_rotations(n: Nat): Circuit<n, n, 2 * (n - 1), Universal> = circuit {
for i in range(n - 1) { controlled(Rz(PI / (2.0 ^ (i + 1)))) @(0, i + 1) }
}
fn qft(n: Nat): Circuit<n, n, 2 * n * n, Universal> =
match n {
0 => identity(0),
_ => apply_hadamard(n)
|> controlled_rotations(n)
|> (qft(n - 1) `on_high` n)
|> swap_reverse(n)
}

The verification program prepares a specific computational-basis state and runs the round trip through it. prep_101 applies X to qubits 0 and 2, preparing the asymmetric basis state |101⟩ — a deliberately non-trivial input that is a stronger check than |000⟩, which every unitary trivially fixes up to global phase. The qft_roundtrip function composes qft(n) with its adjoint adjoint(qft(n)), which the compiler synthesizes by reversing the gate order and negating all rotation angles. If the implementation is correct, this round trip is the identity and |101⟩ is returned unchanged with probability 1.0.

fn prep_101(): Circuit<3, 3, 2, Clifford> = circuit {
X @0 |> X @2
}
fn qft_roundtrip(n: Nat): Circuit<n, n, 4 * n * n, Universal> = circuit {
qft(n) |> adjoint(qft(n))
}
fn main(): Q<List<Bit>> = run {
q <- prep_101() @ qreg(3)
q2 <- qft_roundtrip(3) @ q
measure_all(q2)
}
Terminal window
./target/release/quonc test/verify/qft.qn --emit-qasm > /tmp/qft.qasm
QUONC=target/release/quonc python test/verify/qft.py

The round trip qft(3) |> adjoint(qft(3)) is the optimizer’s playground. The fixpoint pipeline runs gate_cancellation and rotation_merging iteratively:

// After elaboration, qft(3) produces (schematically):
// H @0 |> CRz(pi/2) @(0,1) |> CRz(pi/4) @(0,2) |> [qft(2) on_high 3] |> swap_reverse(3)
// where qft(2) = H @1 |> CRz(pi/2) @(1,2) |> [qft(1) on_high 2] |> swap_reverse(2)
// and qft(1) = H @2
//
// adjoint(qft(3)) is the reverse-ordered conjugate:
// adjoint(swap_reverse) |> adjoint(qft(2)) |> adjoint(CRz) |> adjoint(H)
// where adjoint(CRz(θ)) = CRz(-θ) and adjoint(H) = H (self-adjoint)
//
// gate_cancellation: H @0 |> ... |> H @0 at the boundary —
// the last gate of qft(3) and the first gate of adjoint(qft(3)) may NOT
// be adjacent if swap_reverse sits between them. But after swap_reverse
// is lowered to SWAP gates, the circuit becomes a flat sequence.
//
// After swap_reverse lowering and gate_cancellation to fixpoint:
// H·H → I (self-inverse, adjacent, same wire)
// CRz(θ) · CRz(-θ) → I (not adjacent self-inverse, but rotation_merging
// sees: Rz(θ) followed by Rz(-θ) on the same wire → Rz(0) → I)
//
// The ENTIRE qft |> adjoint(qft) sequence cancels to identity.
// rotation_merging combines each Rz(θ) with its adjoint Rz(-θ) → Rz(0),
// and gate_cancellation erases the resulting identity rotations and
// adjacent H·H pairs. Running to fixpoint, every gate cancels.

This is the strongest demonstration of the optimizer: a non-trivial recursive circuit composed with its adjoint is structurally proven to be the identity. No simulation is needed — the optimizer cancels it purely from gate algebra. The qft.py verifier confirms this: the round trip returns 101 with probability 1.0, and the optimizer’s output is the empty circuit (or just the state preparation and measurement with nothing in between).

  1. Recursive type checking. The match n { 0 => ..., _ => ... } recursion is type-checked with value-dependent types. The typechecker proves that for all n, the recursive case’s depth (1 + 2(n-1) + 2(n-1)^2 + swap) fits within 2n². This is inductive verification of a symbolic depth bound.
  2. on_high embedding is well-typed. qft(n-1) has type Circuit<n-1, n-1, 2(n-1)^2, Universal>. Embedding it on_high into a circuit on n qubits extends the type to Circuit<n, n, ...>. The typechecker proves the qubit indices are valid and the depth is preserved.
  3. adjoint preserves type. adjoint(qft(n)) has the same type as qft(n) — same input/output arity and depth. The typechecker proves this from the adjoint’s definition (reverse gate order, negate rotation angles).
  4. Structural cancellation to identity. The optimizer’s gate_cancellation and rotation_merging passes, run to fixpoint, prove that qft(n) |> adjoint(qft(n)) is structurally equivalent to the identity. This is a compiler proof of the mathematical fact that a unitary composed with its adjoint is the identity — not by simulation, but by gate algebra.

A computational-basis histogram cannot directly reveal the phases produced by a QFT. The fixture therefore prepares |101>, applies qft(3) |> adjoint(qft(3)), and checks that the round trip returns 101 with more than 99% fidelity. The qft.py verifier also reports whether optimization structurally cancels the inverse pair.

  • Remove swap_reverse. The QFT’s output is in bit-reversed order without the permutation. The round trip without swap_reverse on both sides should still cancel, but the measurement may show a different basis state.
  • Change the input state. Try prep_000 (just identity(3)) or prep_111 (X @0 |> X @1 |> X @2). The round trip should still return the input.
  • Apply qft(3) without the adjoint. The output will be a superposition — the histogram will show a spread over all basis states, demonstrating that the QFT actually transforms the state (and that the round-trip cancellation was not trivial).

→ Next: Transverse-field Ising — Trotterize a model and verify its t = 0 boundary.