Chapter 15 — Key Takeaways (Microsoft Q#)
The Q# page. §15.8's T-gate cliff is the thing to remember even if you never write Q#.
Setup
pip install qdk
from qdk import qsharp # NOT `import qsharp` (deprecated)
qsharp.init() # NOT `import qdk` alone -- no eval/run/estimate there
🗝️
qsharpwarnsDeprecationWarning: ... Please use the 'qdk' package instead.Top-levelqdkdoes not re-exporteval/run/estimate— they live inqdk.qsharp.qdkis an umbrella also carryingqdk.qiskit,qdk.cirq,qdk.openqasm,qdk.azure. Verified qdk 1.31.0.
The first program
operation BellPair() : (Result, Result) {
use (a, b) = (Qubit(), Qubit());
H(a);
CNOT(a, b);
let r = (M(a), M(b));
ResetAll([a, b]);
return r;
}
Counter({'(One, One)': 514, '(Zero, Zero)': 486})
operation may touch qubits · use allocates for a scope · let is immutable ·
M returns Result (Zero/One, not a bool or int) · ResetAll is required (see below).
function vs operation — the central distinction
function |
operation |
|
|---|---|---|
| allocate qubits | NO | yes |
| call quantum ops | NO | yes |
| deterministic | yes | no |
function F() : Unit { use q = Qubit(); H(q); }
-> Qdk.Qsc.CallableLimits.QubitAlloc: functions cannot allocate qubits
The classical and quantum halves of a hybrid program are distinguishable by declaration, not by convention you hope everyone follows.
What the compiler refuses
int + double Qdk.Qsc.TypeCk.TyMismatch expected Int, found Double
missing return Qdk.Qsc.TypeCk.TyMismatch expected Unit, found Int
quantum op in a function Qdk.Qsc.CallableLimits.QubitAlloc
swapped argument types Qdk.Qsc.TypeCk.TyMismatch expected Double, found Qubit
undefined variable Qdk.Qsc.Resolve.NotFound `y` not found
reassign an immutable `let` Qdk.Qsc.BorrowCk.Mutability cannot update immutable variable
★ Qubit release discipline
use q = Qubit(); X(q); // never reset
-> Error: Qubit0 released while not in |0⟩ state
Qdk.Qsc.Eval.ReleasedQubitNotZero (with a call stack)
Qubits come from a shared pool; the next allocator is entitled to assume $|0\rangle$. A dirty qubit corrupts whoever gets it next — a bug appearing in a different part of the program from its cause. No other framework here checks this.
⚠️ The check is at SCOPE EXIT, not every reuse. Chapter 9's mid-operation reset hazard (
H,M, no reset,H,M) runs cleanly —Counter({'Zero': 251, 'One': 249})— because the qubit is reset before the block ends.A guarantee is only as broad as its statement. (4th instance: Ch. 11 optimized-away test, Ch. 12 averaged statistic, Ch. 13 inert DD.)
★ Functors for free
operation Rot(q : Qubit) : Unit is Adj + Ctl { H(q); T(q); }
Adjoint Rot(q); // generated
Controlled Rot([c], q); // generated
Rot then Adjoint Rot -> {'Zero': 200} (200 of 200 — the identity)
Omitting the declaration is a compile error:
Qdk.Qsc.TypeCk.MissingFunctor: expected superset of Adj, found empty set
$$(U_1 U_2 \cdots U_n)^\dagger = U_n^\dagger \cdots U_1^\dagger$$
is Adjis a PROOF OBLIGATION, not bookkeeping. An operation containing a measurement has no adjoint, and declaring one anyway is rejected (Qdk.Qsc.LogicSeparation.OpCallForbidden).
The compiler derives the inverse from the definition, so it cannot drift out of sync — unlike a hand-written inverse you must maintain.
★★★ Resource estimation: the T-gate cliff
T gates physical algorithm factories % fact runtime μs # factories
0 450 450 0 0.0% 2.0 0
1 2,882 882 2,000 69.4% 28.0 1
3 12,642 882 11,760 93.0% 36.4 3
10 61,642 882 60,760 98.6% 30.8 10
30 98,658 1,458 97,200 98.5% 111.6 15
100 98,658 1,458 97,200 98.5% 363.6 15
ZERO T gates: 450 qubits. ONE T gate: 2,882. A 6.4× jump for a single gate.
This is Chapter 11's Gottesman–Knill theorem presenting its invoice. Clifford circuits simulate in polynomial time (§11.4) → no advantage. Here, from the hardware side: the gates that make a quantum computer worth building are exactly the gates that make it enormous.
Clifford gates apply transversally, directly on encoded logical qubits.
Tgates need magic state distillation — a factory consuming many noisy states to make one clean $|T\rangle$.
Saturation → space–time trade. 30 and 100 T gates both need 98,658 qubits with 15 factories, while runtime grows 111.6 → 363.6 μs. Beyond a point the estimator reuses factories instead of building more.
One Toffoli = 7 T gates. Not a primitive under error correction.
★★ The parameter that dominates
qubit_gate_ns_e3 4,882 physical qubits 28.0 μs
qubit_gate_ns_e4 180 physical qubits 3.6 μs
qubit_maj_ns_e4 7,818 physical qubits 140.0 μs
qubit_maj_ns_e6 198 physical qubits 18.0 μs
4,882 vs 180 — a factor of 27 — from one order of magnitude in physical error rate.
Code distance $d$ grows as the error rate approaches threshold, and a surface-code patch costs $O(d^2)$. Improving fidelity is quadratically more valuable than adding qubits.
RSA-2048, priced (CS1)
RSA bits logical q T count physical qubits runtime
256 772 20,401,094 2,429,840 3.4 min
1024 3,092 1,309,965,025 11,168,726 4.2 hours
2048 6,189 10,496,900,071 24,937,084 1.5 days
Cross-check: Gidney & Ekerå (2021) — 20M qubits, 8 hours. Same order on both axes, from an independent analysis. The agreement validates the method.
6,189 logical vs 24.9 million physical. Quoting the logical count as a hardware requirement is the most common error in public discussion of this question.
★ The fraction INVERTS with scale. T factories are 93% of a small circuit and 3% of RSA-2048 — long runtimes amortize factories, large registers dwarf them.
A measurement at one scale is evidence about that scale. (3rd instance: Ch. 10 routing overhead, Ch. 11 MPS timing.)
Gap to today: ~1,000 physical qubits now vs ~25 million needed — four to five orders of magnitude, a fidelity and manufacturing problem, not an algorithmic one.
What the compiler catches (CS2)
3 caught + 1 impossible, 8 missed, scored against this book's own documented bugs.
Caught: argument-type confusion (Ch. 6) · missing inverse (Ch. 13) · dirty qubit release. Impossible: Chapter 8's parameter-ordering bug — Q# has no positional-binding-against-sorted-names API at all (neither does Cirq).
Missed: layout trap · QASM phase loss · dead qubit · mis-targeted mitigation · inert DD · endianness · optimized-away test · mid-operation reuse.
Every missed bug is a well-typed program that does the wrong thing. Types catch errors of FORM. Nearly every bug in this book was an error of CORRESPONDENCE — between program and machine, between two conventions, between a technique and the situation.
And the caught ones are the cheap ones to catch by other means; the expensive bugs ran successfully and returned plausible numbers.
"The language does not contain the shape of the mistake" beats "the compiler catches the mistake." API design prevents more bugs than type checking.
When to reach for Q
| Task | Q#? |
|---|---|
| Resource estimation | yes — best in class |
| Large, long-lived codebase | yes |
Many Adjoint/Controlled variants |
yes |
| IBM hardware today · device noise · VQE loops · 50-line scripts | no |
🔬 Unusual recommendation: learn the ESTIMATOR without adopting the language. It consumes logical counts (qubits, T count, measurements) extractable from any framework — which is exactly what this chapter's checkpoint does. Smallest user base of the five; only production-grade estimator.
Common pitfalls
import qsharp(deprecated) orimport qdkalone (noeval/run).- Reading the release check as "prevents qubit-reuse bugs."
- Forgetting
is Adj + Ctl, or declaring it on an operation containing a measurement. - Quoting logical qubit counts as hardware requirements.
- Extrapolating the 93%-factory figure to large algorithms.
- Reading a T count off a circuit that has not been decomposed to Clifford+T.
Project piece added this chapter
vqelab/resources.py — logical_counts() (transpiles to Clifford+T first, so Toffolis reveal
their 7 T gates), estimate_resources(), estimate_circuit(), t_count_sweep(), and a
ResourceEstimate with factory_fraction. Prices any framework's circuit via
qdk.estimator.LogicalCounts — no Q# source required. 10 tests pass, including
test_toffoli_costs_seven_t_gates, test_clifford_only_needs_no_t_factories, and
test_one_t_gate_triples_the_machine.