Every optimization is a claim that two circuits are equivalent. That is what makes this chapter
Prerequisites
- 10
- 12
- 27
Learning Objectives
- Compare preset optimization levels on fidelity rather than gate count.
- Measure whether an optimization helped, with error bars across seeds.
- Use approximation_degree deliberately and understand what it discards.
- Introspect a pass manager to see what actually ran.
In This Chapter
- 28.1 What the levels do
- 28.2 Verifying the equivalence claim
- 28.3 The comparison that is not significant
- 28.4 Levels 2 and 3, and a correction
- 28.5 What the passes actually do
- 28.6 When optimization destroys what you needed
- 28.7 Approximation: trading fidelity on purpose
- 28.8 Where hand optimization still wins
- 28.9 A policy
- What we measured
Chapter 28: Circuit Optimization
Every optimization is a claim that two circuits are equivalent. That is what makes this chapter follow Chapter 27 rather than precede it: without a way to check the claim, an optimizer is a machine for introducing bugs quickly.
The good news arrives immediately. Qiskit's transpiler preserves the unitary exactly at every
optimization level — process fidelity 1.000000000000, verified with Chapter 26's
Operator.from_circuit. The equivalence claim holds.
The hard part is everything after that. Optimization has more than one objective, those objectives disagree, and the metric you improve is not necessarily the metric that determines whether your circuit works. This chapter measures that disagreement, and finds that the most-quoted optimization result in the field — "level 2 beat level 1 on gate count" — was not statistically significant when run on a noisy simulator.
It also contains a correction to a conclusion drawn earlier in the chapter's own drafting, for the usual reason: two examples agreed, so I concluded they always would.
28.1 What the levels do
Qiskit's optimization_level runs from 0 to 3. Measured on FakeSherbrooke (127 qubits, ecr basis),
seed 11:
QFT(5) depth 2q gates total transpile s
level 0 310 56 589 0.12
level 1 137 38 256 0.01
level 2 136 31 232 0.01
level 3 136 31 232 0.01
Grover-ish(5) depth 2q gates total transpile s
level 0 2484 423 4920 0.01
level 1 931 273 1682 0.01
level 2 1080 257 1749 0.01
level 3 1080 257 1749 0.01
EfficientSU2(5, r=3) depth 2q gates total transpile s
level 0 112 12 324 0.00
level 1 40 12 125 0.00
level 2 32 12 105 0.01
level 3 32 12 105 0.01
Level 0 does nothing but make the circuit runnable — basis translation, layout, routing. Level 1 is where most of the win is, and it is nearly free.
Now look at the Grover row again.
level 1 931 273
level 2 1080 257
Level 2 produced a circuit 16% deeper than level 1, using 6% fewer two-qubit gates. That is not a bug and not a regression. It is two objectives disagreeing, and the transpiler resolving them differently at different levels.
Which matters more? Depth drives decoherence — the circuit must finish inside $T_1$ and $T_2$.
Two-qubit gate count drives gate error — Chapter 12 measured a median ecr error around $8\times
10^{-3}$ with a 288× spread. Both are real. They point in opposite directions here.
The only way to answer is to run both.
What "level" actually selects
The four levels are not four settings of a dial. Each one is a different pass manager pipeline, and
the pipeline is readable. On FakeSherbrooke under Qiskit 2.5.1, flattening every stage recursively:
RECURSIVE TASK COUNT BY STAGE
level init layout routing translation optimization scheduling total
0 3 5 4 6 0 4 22
1 5 9 6 6 18 4 48
2 11 7 6 6 20 4 54
3 11 7 6 6 20 4 54
Level 0's optimization stage is empty. Not small — zero tasks. "Level 0 does not optimize" is not a figure of speech about how little it does; it is a structural fact about the pipeline, and it is why level 0 is the right setting whenever you need the circuit you wrote (§28.6).
Read the layout column and notice it is not monotone: level 1 runs nine layout tasks and levels 2
and 3 run seven. More effort is not more passes. The levels differ in which algorithms they select and
how hard each is allowed to try, not in how many boxes get ticked, and a "level" is therefore a curated
opinion rather than a position on a scale.
The optimization stages, deduplicated:
level 1 (18 tasks, 12 distinct)
Optimize1qGatesDecomposition InverseCancellation
Size Depth FixedPoint ContractIdleWiresInControlFlow
GatesInBasis UnitarySynthesis HighLevelSynthesis
BasisTranslator CheckGateDirection GateDirection
level 2 (20 tasks, 14 distinct)
... all of the above, but FixedPoint plus
TwoQubitPeepholeOptimization RemoveIdentityEquivalent
CommutativeCancellation
level 3 (20 tasks, 16 distinct)
... all of level 2's, but MinimumPoint instead of FixedPoint, plus
VF2PostLayout ApplyLayout
Three things are worth extracting from that.
Most of the "optimization" stage is not optimization. GatesInBasis, BasisTranslator,
CheckGateDirection, GateDirection, UnitarySynthesis and HighLevelSynthesis are there to undo
the damage the optimizers do to the basis. A peephole pass will happily emit a u or a raw two-qubit
unitary that FakeSherbrooke cannot execute, and the stage has to translate it back. The optimizers
proper — Optimize1qGatesDecomposition, InverseCancellation, CommutativeCancellation,
RemoveIdentityEquivalent, TwoQubitPeepholeOptimization — are a minority of the list.
Size, Depth and FixedPoint/MinimumPoint are not passes at all, in the sense of transforming
anything. They are the convergence machinery: Size and Depth are analysis passes that record the
current metrics, and FixedPoint decides whether to go round again. This is the loop §28.5's ordering
result predicts you would need.
Level 3 adds two distinct passes and no tasks. Twenty tasks at level 2, twenty at level 3, sixteen distinct names against fourteen. Level 3 is not level 2 run harder; it is level 2 with a different stopping rule and a post-routing layout re-check. §28.4 measures what that buys.
🐛 Debug This: your pass list is full of controllers, not passes.
The obvious way to introspect a preset is to list the tasks in the optimization stage. Do it non-recursively at level 3 and you get:
text ['Size', 'Depth', 'MinimumPoint', 'DoWhileController', 'VF2PostLayout', 'ConditionalController']Six entries, and the two that matter —
DoWhileControllerandConditionalController— are containers. Every optimizer you were looking for is inside them. The recursive version of the same query returns 20 tasks, 16 distinct.The fix is four lines, and it is the same shape as any tree walk:
python def flatten(task, out): if hasattr(task, "tasks"): for sub in task.tasks: flatten(sub, out) else: out.append(type(task).__name__)The failure is quiet and it is worse than useless: a flat listing looks like an answer, so you stop looking. It is the chapter's own §28.4 mistake in miniature — a measurement that cannot see the thing being asked about, returning a plausible result.
🗝️ Version Note: the pass names in that list are Qiskit 2.x names.
TwoQubitPeepholeOptimizationis the pass that replaced the olderCollect2qBlocks→ConsolidateBlocks→UnitarySynthesischain that most published tutorials still show. All three of those classes are still importable in 2.5.1, and if you build a custom pass manager out of them you will get something close to level 2's behaviour by a longer route.
RemoveIdentityEquivalentis the current name;RemoveIdentityEquivalentGatesdoes not exist in 2.5.1 and is the spelling that turns up in older material.Introspection changed too. The private
pm.optimization._tasks[0][0].taskspath used in a lot of older code still works and is not what you should write;pm.optimization.to_flow_controller().tasksis the supported entry point, and it is the one that hands you the controllers described above.The legacy
QFTandEfficientSU2classes also still import in 2.5.1 alongside theQFTGate/efficient_su2forms used throughout this book. They transpile to different circuits in some cases, so if you are reproducing a published gate count, check which one the author used.
28.2 Verifying the equivalence claim
Before comparing performance, check the claim. Chapter 26 §26.7's technique, applied at every level:
level 0: depth 13 2q 7 process_fidelity 1.000000000000 PRESERVED
level 1: depth 12 2q 7 process_fidelity 1.000000000000 PRESERVED
level 2: depth 12 2q 7 process_fidelity 1.000000000000 PRESERVED
level 3: depth 12 2q 7 process_fidelity 1.000000000000 PRESERVED
Exact, at every level. Operator.from_circuit applies the initial layout and the routing permutation;
without it you get Chapter 26 Case Study 2's 0.001406 and a week of misdirected debugging.
This check belongs in your test suite, not in a one-off script. Chapter 27 §27.8 priced it at about
13 ms — 4,701 assertions per CI-minute — and the reason to pay is not that the transpiler is buggy but
that your own configuration is the thing most likely to be wrong: a bad initial_layout, a
basis_gates list missing something you assumed, an optimization level that removed something you
needed. §28.5 is about that last one.
And the limit is Chapter 26's: on a 127-qubit backend the operator cannot be built at all. Verify on a small backend with similar coupling structure, then apply the settings to the large one.
28.3 The comparison that is not significant
Level 1 is shallower; level 2 uses fewer two-qubit gates. Which circuit actually produces better results?
Run both on a noisy simulator built from FakeSherbrooke, across 8 transpiler seeds, 20,000 shots
each. The circuit is a Grover-like search whose noiseless answer is 11111 with probability 0.6027:
level depth mean ecr mean P(top) mean std best
1 956.4 279.8 0.0917 0.0119 0.1031
2 1098.6 254.0 0.0945 0.0140 0.1072
difference (L2 - L1) = +0.0028 +/- 0.0065 NOT SIGNIFICANT at 2 sigma
L2 is +15% on DEPTH and -9% on ECR COUNT.
The +9% improvement in two-qubit gate count bought nothing measurable, because it was paid for with a 15% increase in depth. The two effects roughly cancel.
🔬 Honest Assessment: a gate-count improvement is not a result.
"Level 2 reduced two-qubit gates by 9%" is true and is what almost every optimization report says. The measured effect on output fidelity was $+0.0028 \pm 0.0065$ — consistent with zero.
Optimizing a proxy metric is only useful insofar as the proxy predicts the thing you care about, and here two standard proxies predicted opposite outcomes. Report the fidelity, or report that you did not measure it.
This is the same structure as Chapter 25's team reporting a 50× improvement factor when the decision needed a breakeven, and Chapter 21's Grover benchmarked against a strawman. The number that is easy to get is not the number that answers the question.
Deriving the cancellation
A null result is more useful when you can say why the effects cancelled, because then you know where the cancellation stops holding.
📐 Math Aside: the exchange rate between a two-qubit gate and a layer of depth.
Model the two mechanisms separately. Two-qubit gate error is multiplicative in the gate count, so with per-gate error $p$ and $n$ two-qubit gates the surviving amplitude goes as
$$F_{\text{gate}} = (1-p)^{n}$$
Decoherence is exponential in elapsed time, and elapsed time is depth $d$ times the duration of a layer $\tau$, against a coherence time $T$:
$$F_{\text{deco}} = e^{-d\tau/T}$$
Level 2 improves the first and damages the second. Using Chapter 12's median
ecrerror $p = 7.8\times 10^{-3}$ and the measured means $n_1 = 279.8$, $n_2 = 254.0$:
text gate-error gain = (1-p)^(254.0 - 279.8) = (0.9922)^(-25.8) = 1.2239Level 2 should have gained 22% on the gate-error term alone. It measured $+3\%$ ($0.0945/0.0917 = 1.0305$), so the depth increase ate essentially all of it. Setting the two equal:
$$e^{-\Delta d\,\tau/T} = \frac{1}{1.2239} \quad\Longrightarrow\quad \Delta d\,\frac{\tau}{T} = \ln 1.2239 = 0.2020$$
With $\Delta d = 1098.6 - 956.4 = 142.2$ layers, that fixes $\tau/T = 1.421\times10^{-3}$ per layer. Chapter 39 measured $T_1$ on a real device from 15.2 to 483.0 µs, which puts the implied layer duration at:
text T = 50 us -> tau = 71 ns T = 100 us -> tau = 142 ns T = 175 us -> tau = 249 ns (Ch.39's measured median T1) T = 483 us -> tau = 686 nsChapter 39 also measured the gates themselves:
cz68–184 ns,sx32–64 ns. The implied layer time lands inside the measured gate durations across most of the coherence range. The cancellation is not a coincidence and not a fluke of this circuit — it is what you get when a device's layer time and coherence time stand in this ratio.The useful form of the result is the exchange rate itself. Rearranged, this circuit and this noise model price
$$\frac{\Delta n_{2q}}{\Delta d} = \frac{25.8}{142.2} = 0.181 \quad\Longrightarrow\quad > \textbf{1 two-qubit gate} \approx \textbf{5.5 layers of depth}$$
Now the trade is decidable in advance. A transpiler setting that removes one two-qubit gate at a cost of two layers is worth taking; one that costs ten layers is not. And a device with faster gates or longer coherence moves the rate — which is why this number, unlike "9% fewer gates", is a claim about this device and says so.
📉 Noise Report: the gate-count model over-predicted, and by how much.
Chapter 30 §30.4 established that the median two-qubit error is predictive: $(1-0.00750)^{257} = 0.1444$ against this chapter's measured $0.1290$, within 12%. So the model works on absolute fidelity. Ask it for the difference between levels and it fails:
text model, Ch.30 median p = 0.00750 predicted L2 - L1 = +0.0197 model, Ch.12 median p = 0.0078 predicted L2 - L1 = +0.0205 MEASURED +0.0028 +/- 0.0065The pure gate-count model sits 2.6σ above the measurement. It is not a bad model; it is a model with one term, applied to a comparison where the missing term is the one that moved.
A model that predicts a level well can predict a difference badly, because the difference is exactly where the omitted mechanism lives. Chapter 30's own advice — use the median for planning, stop the moment you pin a layout — is the same caution one step earlier.
There is a consistency check available, and it is a good one. If the entire loss at level 1 were two-qubit gate error at a uniform rate, that rate would have to be
text 1 - (0.1293)^(1/279.8) = 0.00729against Chapter 30's measured median of 0.00750 and Chapter 12's ~0.0078. The whole budget is essentially accounted for by two-qubit gates at the median rate — which is why Chapter 30's one-parameter model got within 12%, and why it has nothing left over to explain a depth change.
What is significant
level 0: P(11111) = 0.0390 vs noiseless 0.6027 -> 6.5% of the signal retained
level 1: P(11111) = 0.0779 vs noiseless 0.6027 -> 12.9% of the signal retained
level 2: P(11111) = 0.0778 vs noiseless 0.6027 -> 12.9% of the signal retained
Level 0 to level 1 roughly doubles the surviving signal. That is a large, unambiguous, free win, and it is the one that matters. Everything above level 1 is refinement.
And note the absolute number: the best circuit recovers 12.9% of the noiseless signal. Optimization is worth doing and it does not come close to rescuing this circuit. Chapter 25 explained why — the device is above the threshold where any of this becomes reliable.
How many seeds would have settled it
The error bar in §28.3 is not a decoration; it is computable in advance, and computing it tells you whether the experiment you are about to run can answer the question you are about to ask.
The standard error of the difference of two means over $n$ seeds is
$$\mathrm{SE} = \sqrt{\frac{\sigma_1^2 + \sigma_2^2}{n}}$$
which with the measured $\sigma_1 = 0.0119$ and $\sigma_2 = 0.0140$ at $n = 8$ gives $0.006496$ — the $\pm 0.0065$ printed above. To call a difference of $+0.0028$ significant at $2\sigma$ you need $\mathrm{SE} \le 0.0014$, so
$$n \ \ge\ \frac{\sigma_1^2 + \sigma_2^2}{(0.0028/2)^2} = \frac{3.3761\times10^{-4}}{1.96\times10^{-6}} = 172.3$$
to resolve L2 - L1 = +0.0028 ...
at 1 sigma: 44 seeds
at 2 sigma: 173 seeds 21.6x the 8 we ran
6,920,000 shots
173 seeds. That is the honest price of turning this into a result, and stating it is more useful than the result would be.
Because the right conclusion is not "go and run 173 seeds." It is that the 8-seed study already produced a result — an upper bound. Whatever the level-2 effect is, it is smaller than $2\times 0.0065 = 0.0130$, which is under 14% of level 1's own mean. In effect-size terms the observed difference is $0.216$ pooled standard deviations, and an effect that small is not what is standing between this circuit and a usable answer.
An underpowered experiment that bounds an effect is not a failed experiment. It is a failed experiment only if you report the sign of the mean and call it a finding.
💰 Cost and Queue: what the honest version costs, and why it was affordable.
This comparison ran on a noise model, where the only currency is wall clock. Price it as though it had run on hardware, using Chapter 33's measured throughput — 27.8 QPU hours per million predictions at 1,000 shots, i.e. $10^9$ shots in 27.8 h, or 9,992 shots/s — and Chapter 39's $50-per-minute tier:
text the 8-seed study 320,000 shots 32.0 s 0.53 min $26.69 the 173-seed study 6,920,000 shots 692.6 s 11.54 min $577.13$27 to bound the effect. $577 to resolve it. Neither is expensive, and that is the point: the reason nobody runs this experiment is not cost.
Two cautions on the arithmetic. Chapter 33's throughput is for a shallower circuit, so treat these as floors. And Chapter 39 §39.3 measured utilization at a five-minute queue of $2.31\times10^{-5}$ — 43,340× wall clock — so eleven minutes of execution is not eleven minutes of your day. Batch the 346 circuits into one job; Chapter 39 measured roughly 99× from batching 100 circuits.
The seed matters more than the level
Put the two sources of variation side by side.
between LEVELS (L2 - L1) 0.0028
within a level, across SEEDS L1 std 0.0119 4.25x
L2 std 0.0140 5.00x
best L1 seed above L1's mean 0.1031 0.0114 4.1x
The choice of transpiler seed moves the answer four to five times as much as the choice of optimization level. The comparison everyone runs is the smaller of the two knobs.
This is Chapter 10 §10.9's point arriving with a price tag, and Chapter 39 §39.6 measured the same
thing at scale: a 14-qubit EfficientSU2 transpiled with 24 seeds ranged from 0.5755 to 0.7911 —
2.03× the error — with two-qubit counts from 49 to 112. Nothing changed but an integer.
So if you have a fixed budget of transpiler runs, spend it on seeds at one level rather than on comparing levels. Transpile eight times at level 2, keep the circuit with the fewest two-qubit gates, and you will have gained more than the level-2-versus-level-1 question could ever have given you.
Two honest caveats on that advice.
Best-of-$n$ is a selection, and selections are biased high. The seed that measured best is partly the seed that got lucky. Re-measure your top two or three candidates before committing — the seed choice is reproducible in a way a lucky shot draw is not, so the re-measurement is cheap and settles it.
And seed variance is a large-circuit phenomenon. Chapter 39 ran the identical 24-seed test on a 4-qubit circuit and got exactly zero variation — the transpiler found the same good layout every time. A team that measures seed sensitivity on a toy circuit concludes the seed does not matter, which is §28.4's error in another costume.
Both knobs are still small next to the one Chapter 29 measures. Hardware-aware circuit design at level 1 (0.9116) beat a naive circuit at level 3 (0.7720) by $+0.1397$ — fifty times the level effect measured here. Ordering the three: how you build the circuit ≫ which seed you transpile with > which level you ask for.
28.4 Levels 2 and 3, and a correction
While drafting §28.1 I noticed that levels 2 and 3 produced byte-identical output on both test circuits, checked the instruction sequences, confirmed they matched, and wrote that Qiskit had unified the two levels and that "always use level 3" was now folklore.
That was wrong, and it was wrong for a reason this book keeps rediscovering: two examples agreed, so I concluded they always would.
Widening to five circuits × eight seeds:
QFT(5) differed in 0/8 seeds
QFT(8) differed in 6/8 seeds
Grover-ish(5) differed in 0/8 seeds
Grover-ish(7) differed in 8/8 seeds
EffSU2(6, r=4) differed in 0/8 seeds
TOTAL: levels 2 and 3 differed in 14/40 trials.
The two small circuits I happened to test are exactly the ones where they agree. On larger circuits they differ almost always. And when they differ, level 3 usually wins:
circuit seed L2 depth L3 depth L2 ecr L3 ecr winner
QFT(8) 1 329 285 94 94 L3
QFT(8) 2 272 291 87 91 L2
QFT(8) 3 322 324 102 87 L3
QFT(8) 7 343 318 94 104 L2
Grover-ish(7) 0 1784 1776 476 476 L3
Grover-ish(7) 5 1816 1777 459 456 L3
Of the 14 differing trials: L3 better 12, L2 better 2.
The pass lists confirm the levels are genuinely different:
level 1: Optimize1qGatesDecomposition, InverseCancellation, ...
level 2: + TwoQubitPeepholeOptimization, RemoveIdentityEquivalent,
CommutativeCancellation
level 3: same as level 2, but MinimumPoint instead of FixedPoint for the
convergence loop, plus VF2PostLayout and ApplyLayout
MinimumPoint keeps iterating and returns the best circuit seen rather than stopping at the first
fixed point; VF2PostLayout re-examines the layout after routing using measured error rates. Both are
real, and both only have room to act on circuits large enough to have alternatives.
⚠️ Common Pitfall: concluding "always" from two examples.
The circuits where two configurations agree are, by definition, the circuits that cannot tell them apart. Testing on them is Chapter 27's blind-input problem wearing different clothes — and Chapter 26 §26.4 measured that structured, convenient test cases are blind 41% of the time.
Vary the circuit as well as the seed.
Why MinimumPoint can only help — on the metric it tracks
The single structural difference between the two levels is the stopping rule on the optimization loop, and it is worth being precise about what that rule does, because "level 3 tries harder" is not what is happening.
The optimization stage is a DoWhileController wrapped around the optimizers, with Size and Depth
recording the circuit's metrics on each pass. The controller needs to know when to stop.
FixedPoint(prop) stop when prop stops CHANGING between iterations
MinimumPoint(props) keep going past a plateau, and return the BEST
circuit seen rather than the last one
The loop is not monotone, and that is the whole reason the distinction exists.
TwoQubitPeepholeOptimization collects a two-qubit block and resynthesises it; the resynthesis can be
larger than what it replaced, and only on the following iteration does CommutativeCancellation find
the cancellations that resynthesis exposed. A metric that goes down, up, then down again defeats a rule
that stops the first time it fails to move.
The argument that MinimumPoint cannot lose is a one-liner. Given the same trajectory, it runs at
least as many iterations as FixedPoint, so the set of circuits it evaluates is a superset, and it
returns the minimum over that set. A minimum over a superset is no worse.
But read the guarantee carefully: it is a guarantee about size and depth, which are the properties
the loop tracks. It is not a guarantee about two-qubit gate count, and it is certainly not a guarantee
about fidelity. That is this chapter's thesis appearing inside the transpiler's own control flow — the
convergence loop optimizes a proxy, and the proxy is the one that is cheap to compute after every pass.
That is enough to explain the two trials level 3 lost. Look at seed 7:
QFT(8) seed 7 L2 depth 343 L3 depth 318 L2 ecr 94 L3 ecr 104
Level 3 returned a shallower circuit with ten more two-qubit gates. The loop did exactly what it promised — it minimised depth — and the table's winner rule scores two-qubit count first. Nothing malfunctioned. Two objectives disagreed again, one level down.
And the guarantee is void anyway once VF2PostLayout is in the pipeline, because re-examining the
layout after routing does not extend level 2's trajectory — it starts a different one. Level 3 is not
level 2 plus more iterations. It is a different search.
🔬 Honest Assessment: 12–2 is a weaker result than it reads.
Treat the 14 differing trials as independent coin flips and 12–2 is significant: 106 of 16,384 outcomes are that lopsided or worse, giving $p = 0.0065$ one-sided, $0.013$ two-sided.
They are not independent. The 14 trials come from exactly two circuits — QFT(8) contributed 6 and Grover-ish(7) contributed 8 — and both of level 3's two losses sit in the same family, the QFT(8) rows at seeds 2 and 7. Score it by circuit rather than by trial and level 3 wins 2–0, with $p = 0.5$ two-sided. Two samples.
Which is the sample size that produced this section's error in the first place. The correction to a two-sample conclusion should not itself rest on two samples, and §28.9's "prefer level 3" is therefore a weak preference — free to act on, not established.
The stronger claim is the one that does not depend on the win rate at all: levels 2 and 3 are genuinely different pipelines, they diverge on 35% of circuit-seed pairs and on 14 of 16 pairs drawn from circuits large enough to have alternatives, and anything you concluded from small circuits transfers to neither.
28.5 What the passes actually do
The preset levels are compositions of individual passes, and you can run them alone. On a
deliberately wasteful circuit — H H, CX CX, three RZs summing to zero, X X, and an RZ
sandwiched between two CXs it commutes past:
original {'cx': 4, 'rz': 4, 'h': 2, 'x': 2} depth 8
InverseCancellation(H,CX,X) {'rz': 4} depth 4 fid 1.000000
Optimize1qGatesDecomposition {'cx': 4, 'u1': 1} depth 4 fid 1.000000
CommutativeCancellation {'rz': 1} depth 1 fid 1.000000
RemoveIdentityEquivalent {'cx': 4, 'rz': 4, 'h': 2, 'x': 2} depth 8 fid 1.000000
ALL FOUR, forward order {'u1': 1} depth 1 fid 1.000000
ALL FOUR, reversed {'rz': 1} depth 1 fid 1.000000
Three observations worth having.
CommutativeCancellation alone does the whole job here — depth 8 to depth 1, better than the
other three individually. It is the pass that understands that an RZ commutes through a CX's
control, which is where the non-obvious cancellations live.
RemoveIdentityEquivalent did nothing, because none of these gates is individually close enough
to the identity for its threshold; the RZs only sum to zero collectively. A pass that does nothing
on your circuit is not a broken pass — different passes see different structure.
Order changes the output. Forward gives u1, reversed gives rz — the same depth and the same
unitary, in a different basis. On larger circuits ordering changes counts too, which is why the preset
pass managers wrap their optimization stage in a convergence loop rather than running each pass once.
And every fidelity is exactly 1.000000. The passes are equivalence-preserving by construction. What they are not is ordering-independent.
Why an RZ commutes through a CX control
CommutativeCancellation won because it can move gates that the other passes can only look at.
InverseCancellation matches adjacent inverse pairs; CommutativeCancellation first slides gates
past everything they commute with, and then matches. Everything depends on knowing what commutes with
what, so it is worth deriving.
📐 Math Aside: the control line passes $Z$, the target line passes $X$.
Write the
CXas a projector-conditioned flip:$$\mathrm{CX} = |0\rangle\!\langle 0|_c \otimes I_t \;+\; |1\rangle\!\langle 1|_c \otimes X_t$$
and take an
RZon the control, which is diagonal in that same basis:$$R_Z(\theta)_c = e^{-i\theta/2}|0\rangle\!\langle 0|_c + e^{+i\theta/2}|1\rangle\!\langle 1|_c$$
Multiply in both orders. Each term picks up the scalar belonging to its own projector, and scalars do not care about ordering:
$$R_Z(\theta)_c\,\mathrm{CX} \;=\; e^{-i\theta/2}|0\rangle\!\langle 0|\otimes I + e^{+i\theta/2}|1\rangle\!\langle 1|\otimes X \;=\; \mathrm{CX}\,R_Z(\theta)_c$$
Exactly equal, for every $\theta$. Put the same rotation on the target and it fails, because $R_Z(\theta)X \ne X R_Z(\theta)$ — the flip lands you on the other diagonal entry.
The general statement is cleaner in exponential form. With $P_1 = (I - Z_c)/2$,
$$\mathrm{CX} = \exp\!\Big(i\frac{\pi}{2}\,P_1 \otimes (I - X_t)\Big)$$
so anything commuting with $Z$ on the control, or with $X$ on the target, commutes with the whole gate. Checked numerically at $\theta = 0.5$:
text gate on CONTROL on TARGET RZ True False Z True False RX False True X False True H False --Nine algebraic facts, and they are the whole content of the pass. A
CXreads its control in the $Z$ basis and writes its target along $X$; a rotation about the axis a wire is already committed to is invisible to the gate.
That is why CommutativeCancellation reached depth 1 while InverseCancellation stopped at depth 4.
The three RZ rotations summing to zero were not adjacent — there was other structure between them —
and only a pass that can slide a rotation along the wire brings them together to be merged.
It is not free. The relations above have to be checked for every pair the pass considers, which is why
CommutativeCancellation appears at level 2 and not level 1, and where part of the measured level-1 to
level-2 transpile cost goes.
⚛️ The Physics Underneath: an
RZon a superconducting qubit is not a pulse.The commutation rule has a physical shadow that makes it more useful than it looks. Chapter 31 measured the duration of an
rzon real hardware as 0.0 ns — a $Z$ rotation is implemented as a frame change, a bookkeeping update to the phase reference of every subsequent pulse on that qubit. No microwave is emitted. Nothing decoheres that would not have decohered anyway.So
rzgates are simultaneously free to execute and free to move, and the optimizer exploits both properties. They cost no time, so accumulating them costs nothing; they commute along the control line, so they can be swept together and merged; and merging $k$ of them into one costs nothing either, because one frame change and $k$ frame changes take the same zero nanoseconds.This is why the basis of
FakeSherbrookeis built the way it is.rzis free,sxis a real pulse,ecris an expensive real pulse, and every single-qubit optimization in the transpiler is ultimately a scheme for pushing work out ofsxand intorz.Optimize1qGatesDecompositionrewriting a run of gates as $R_Z\,\sqrt{X}\,R_Z\,\sqrt{X}\,R_Z$ is not choosing a pretty normal form; it is choosing the form with the fewest things that actually happen.
28.6 When optimization destroys what you needed
Optimization removes gates that do nothing. Sometimes a gate that does nothing is load-bearing.
H H (no barrier) -> {} depth 0
H barrier H -> {'h': 2, 'barrier': 1} depth 2
The first circuit was annihilated — correct, since $HH = I$, and catastrophic if those Hadamards were there to hold a qubit in the X basis for a fixed duration.
A barrier is the only reliable instruction to the optimizer not to combine things across a point. Use it around Chapter 13's zero-noise-extrapolation folds, around dynamical-decoupling sequences, and around anything whose timing rather than unitary is the point.
But barriers are weaker than they look:
Ch.25's noise slot, bare: {'id': 1, 'measure': 1} -> {'measure': 1}
Ch.25's noise slot, barriered: -> {'barrier': 2, 'measure': 1}
The barriers survived and the id was still deleted. A barrier stops gates commuting across it;
it does not stop a single removable gate between two barriers from being removed. Chapter 25's fix —
optimization_level=0 plus an assertion on the gate count — remains the only reliable one. A delay
instruction is the other option, since it carries a duration and is therefore not a no-op, but it
requires the scheduling passes to have run.
⚠️ Common Pitfall: the obvious protection does not work. "Wrap it in barriers" is the standard advice and it does not save an
idgate. Assert the gate count after transpilation — Chapter 26 §26.5'scount_opsdiff, which costs nothing.🧪 Run It: find out what your own protection is worth.
Build the same one-qubit noise slot four ways and diff the counts at levels 0, 1, 2 and 3:
```python from qiskit import QuantumCircuit, transpile from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
BE = FakeSherbrooke() variants = {} for name in ("bare", "barriered", "delayed", "dd"): qc = QuantumCircuit(1, 1) if name == "bare": qc.id(0) if name == "barriered": qc.barrier(); qc.id(0); qc.barrier() if name == "delayed": qc.delay(200, 0, unit="dt") if name == "dd": qc.barrier(); qc.x(0); qc.x(0); qc.barrier() qc.measure(0, 0) variants[name] = qc
for name, qc in variants.items(): before = dict(qc.count_ops()) for lvl in (0, 1, 2, 3): after = dict(transpile(qc, BE, optimization_level=lvl).count_ops()) lost = {k: v - after.get(k, 0) for k, v in before.items() if v - after.get(k, 0) > 0} print(f"{name:<10} L{lvl} {str(after):<48} lost {lost or '-'}") ```
Three questions the output answers that this section did not measure:
Does the
delaysurvive? The claim above is that it should, because a duration is not a no-op — but it is a claim, and it may depend on whether the scheduling stage ran. Check it, and check it at level 3 as well as level 1.Does an
X Xdynamical-decoupling pair inside barriers survive? It is the same $UU^\dagger = I$ shape asH barrier H, but with the barriers outside the pair rather than between it. §28.6 says that placement is the one that fails.And which level kills each one? If a protection survives level 1 but not level 3, you have a configuration that works until someone tunes it — the worst kind of latent bug, and exactly the reason Chapter 27 §27.8 wanted the assertion in the suite rather than in a notebook.
Whatever you find, encode it.
slots_survived(circuit, backend, level, instruction, expected)from this chapter's project module is nine lines and turns each answer into a test.
28.7 Approximation: trading fidelity on purpose
Everything so far preserved the unitary exactly. Qiskit's approximation_degree deliberately does not,
synthesizing two-qubit blocks to lower fidelity in exchange for fewer gates.
On FakeManilaV2 — 5 qubits, so the operator is buildable and the fidelity is checkable:
approximation_degree 2q gates depth process fidelity
1.0 19 36 1.000000
0.99 15 29 0.925328
0.95 8 13 0.657023
0.9 0 3 0.455317
0.8 0 3 0.455317
0.5 0 3 0.117562
Read the fourth row. At approximation_degree=0.9 the QFT has zero two-qubit gates. The entire
entangling structure has been synthesized away, leaving a depth-3 circuit with 46% fidelity to the
thing you asked for.
🔬 Honest Assessment:
approximation_degreeis not a gentle dial, and it is not a fidelity.
0.99does not mean "99% fidelity" — it produced 0.925. And the parameter falls off a cliff: between 0.95 and 0.9 the circuit goes from 8 two-qubit gates to none at all.Never set it without measuring the fidelity you traded away.
Why zero two-qubit gates is the logical endpoint
The cliff looks like a bug. It is the opposite: it is the parameter doing exactly what the underlying mathematics permits, which is why no amount of care in choosing the value will smooth it out.
Every two-qubit unitary factors as local operations around a purely non-local core — the Cartan or KAK decomposition:
$$U \;=\; (A_1 \otimes A_2)\;\exp\!\big(i(a\,XX + b\,YY + c\,ZZ)\big)\;(B_1 \otimes B_2)$$
The four $A$ and $B$ factors are single-qubit gates and cost nothing on the two-qubit budget. Everything
entangling lives in the three interaction coefficients $(a, b, c)$, and the number of cx or ecr
gates the block needs is set by how many of them are non-zero:
non-zero coefficients two-qubit gates required
3 3
2 2
1 1
0 0 <- a product operator
That is the whole ladder. It has four rungs and no intermediate positions, because you cannot spend
half an ecr.
approximation_degree is the tolerance for calling a coefficient close enough to zero to drop. So it
does not tune a continuous fidelity; it walks down a four-rung integer ladder, one block at a time,
and the fidelity is whatever falls out. A parameter that controls an integer cannot be smooth in the
quantity you care about, and the last rung — all three coefficients discarded, a product of single-qubit
gates, zero entanglement — is not an extreme setting or a failure. It is simply the bottom of the
ladder, reachable in one step from the rung above it.
1.0 19 two-qubit gates every block keeps all three coefficients
0.99 15 some blocks drop to a lower rung
0.95 8 more blocks drop further
0.9 0 every block is now a product operator
Read the bottom two rows of §28.7's table again, though, because they contain the sharper lesson.
0.9 0 3 0.455317
0.8 0 3 0.455317
0.5 0 3 0.117562
At 0.9, 0.8 and 0.5 the circuit has identical two-qubit count and identical depth — and the fidelity falls by a factor of 3.87. Once the entangling ladder bottoms out the parameter starts discarding the local factors as well, and no structural metric can see it happen. A gate-count monitor watching this sweep would report that nothing changed after 0.9.
The gate count stopped moving three rows before the fidelity did. That is the chapter's thesis in its purest form: the proxy went flat while the thing it was standing in for kept falling.
The arithmetic of the trade, all the way down
Case Study 2 works the $1.0 \to 0.99$ step and finds it a net loss. Work every row. Against Chapter 12's
median ecr error $p = 7.8\times10^{-3}$, removing $k$ two-qubit gates multiplies the surviving
amplitude by $(1-p)^{-k}$, and the approximation costs a factor of $F$:
ad 2q removed fidelity F gate-error gain net = F(1-p)^-k
1.0 19 0 1.000000 1.0000 1.0000
0.99 15 4 0.925328 1.0318 0.9548 LOSS
0.95 8 11 0.657023 1.0900 0.7161 LOSS
0.9 0 19 0.455317 1.1604 0.5284 LOSS
0.5 0 19 0.117562 1.1604 0.1364 LOSS
Every row is a net loss on this device. And the breakeven condition says why. To pay for a fidelity factor $F$ you must remove
$$k \;\ge\; \frac{-\ln F}{-\ln(1-p)}$$
two-qubit gates, which for these fidelities means:
F = 0.925328 -> need to remove 9.9 gates (0.99 removes 4)
F = 0.657023 -> need to remove 53.6 (0.95 removes 11)
F = 0.455317 -> need to remove 100.5 (0.9 removes 19)
The first useful step already demands more gates than a quarter of the circuit, and the circuit only
contains 19. On this circuit and this device, no setting of approximation_degree can win — not
because the idea is wrong, but because the arithmetic does not close.
📊 What the Numbers Say: where the trade flips, exactly.
Invert the breakeven instead. Holding the $1.0 \to 0.99$ step fixed at "remove 4, pay 0.925328", what per-gate error rate $p$ would make it worthwhile? Solve $(1-p)^{-4} = 1/0.925328$:
text ad = 0.99 (remove 4, pay 0.925328) breaks even at p = 0.0192 ad = 0.95 (remove 11, pay 0.657023) breaks even at p = 0.0375 ad = 0.9 (remove 19, pay 0.455317) breaks even at p = 0.0406Now compare against Chapter 30's measurement of a real chip's two-qubit error distribution: min 0.00347, median 0.00750, mean 0.01018, max 0.11736, with 9 of 144 edges dead.
$p = 0.0192$ is between that chip's mean and its maximum. A circuit routed over the bad half of the same device makes the 0.99 trade a win. Case Study 2's team were not wrong about the principle; they were wrong about which edges they were running on, and they never measured either side.
This is the shape of every honest optimization decision in this chapter: not "does it help?" but "at what error rate does it start helping, and where am I on that scale?" The first question has no stable answer. The second one does.
And Chapter 26's wall makes that hard exactly where you would want it. On a 127-qubit backend the fidelity column above cannot be computed — the operator does not fit in the universe. Do the approximation study on a small backend, choose a setting, then apply it. The setting transfers; the verification does not.
There is a legitimate use. When the noise floor is already at 87% (§28.3's level-1 circuit retains 13% of its signal), a synthesis that costs 7% fidelity to remove 20% of the two-qubit gates may be a net win. That is an empirical question with an empirical answer, and the answer requires running both.
28.8 Where hand optimization still wins
The transpiler is a peephole optimizer with a routing pass. It works on the circuit you give it, and it cannot restructure your algorithm.
It cannot choose a better decomposition of a high-level operation. Chapter 19 found that a plain
MCXGate with spare qubits available beat a hand-specified v-chain — because leaving the transpiler
room let HighLevelSynthesis pick. That is a hand optimization: not doing the work yourself.
It cannot know your ancillas are free. Chapter 19's ancilla-count trade — more qubits for lower depth — is a decision only you can make, because only you know whether those qubits are available.
It cannot exploit your problem structure. Chapter 22's AQFT drops rotations below a cutoff, which is a numerical argument about phase precision, not a circuit identity. No peephole optimizer will find it.
And it does not optimize $T$ count, which is the metric that matters after error correction. Chapter 15's resource estimates were dominated by $T$ gates and Chapter 25 §25.10 explained why: the surface code supports Clifford gates natively and $T$ gates only through magic state distillation. Depth and two-qubit count are the pre-fault-tolerant metrics. $T$ count is the post-fault-tolerant one, and optimizing one does not optimize the other.
Where more optimization hurts
Gather this chapter's own counterexamples into one list, because the default assumption — that a higher number is a safer number — survives an astonishing amount of contrary evidence.
1. LEVEL 1 -> 2 Grover-ish(5): 16% DEEPER for 6% fewer two-qubit gates
2. LEVEL 2 -> 3 lost 2 of the 14 trials where they differed;
QFT(8) seed 7 came back shallower with 10 MORE ecr gates
3. LEVEL 0 -> 1 deleted Chapter 25's `id` noise slot
4. LEVEL 3 annihilated `H H` to depth 0
5. approximation deleted the entangling structure entirely at 0.9
6. TRANSPILE TIME level 3 costs 1.4x level 1 at 5 qubits, 1.8x at 14
Six failures, and they are not the same kind of failure. They sort into four.
Harm by objective conflict (1, 2). More effort on one metric, less on another, both metrics real. Unavoidable, undetectable from either metric alone, and settled only by measuring output fidelity with an error bar. This is the chapter's central case and the one nobody checks.
Harm by correctness (3, 4, 5). The optimizer removed something that was load-bearing for a reason
the optimizer cannot see. Entirely preventable, and preventable cheaply: a count_ops diff, an
assertion, a barrier where a barrier works, optimization_level=0 where it does not.
Harm by cost (6). Measured on FakeSherbrooke, best of three runs per cell:
QFT(n), seed 11 L0 s L1 s L2 s L3 s L3/L1
n = 5 0.003 0.006 0.008 0.008 1.4x
n = 8 0.005 0.007 0.009 0.010 1.4x
n = 11 0.005 0.008 0.011 0.012 1.4x
n = 14 0.006 0.011 0.015 0.020 1.8x
At these sizes transpile time is not a reason to choose a lower level. Twenty milliseconds at 14 qubits is nothing, and the ratio grows slowly. It becomes a reason in exactly one situation: a variational loop that re-transpiles every iteration. Chapter 39 measured 120 VQE iterations taking 10 hours as separate jobs against 5 minutes in a session, and a per-iteration transpile is the same mistake in the same place. Chapter 10 §10.10 is the fix — transpile once, bind many — and it removes the cost entirely rather than trading it against optimization quality.
And harm by displacement, which is the one that has no entry in the table because nothing measures it. Every hour spent on the level flag is an hour not spent on the circuit. Chapter 29 measured hardware-aware design at level 1 (0.9116) beating naive design at level 3 (0.7720): a gap of $+0.1397$, roughly fifty times the level-2-versus-level-1 effect measured here. The transpiler flag is the most visible knob and close to the least valuable one, and its visibility is precisely why it absorbs the attention.
The easy number is almost always the flattering one — because it stops the search.
🔀 In Another Framework: nobody else ships an "optimization level".
Cirq 1.7.0 has no preset levels at all. You compose the pipeline yourself:
cirq.optimize_for_target_gateset(circuit, gateset=cirq.CZTargetGateset())for the translation, then transformers by hand —cirq.eject_z(which is precisely §28.5's commutation trick: push $Z$ rotations rightward along the wire and merge them),cirq.eject_phased_paulis,cirq.merge_single_qubit_gates_to_phxz(Qiskit'sOptimize1qGatesDecomposition),cirq.drop_negligible_operations(Qiskit'sRemoveIdentityEquivalent), thencirq.drop_empty_momentsandcirq.align_leftorcirq.stratified_circuitfor the scheduling. Chapter 14 has the details.PennyLane 0.45.1 is the same shape:
qml.compile(pipeline=[...])overqml.transforms.cancel_inverses,merge_rotations,commute_controlled,single_qubit_fusion,undo_swaps,remove_barrier,pattern_matching_optimization. Note the namecommute_controlled— it is §28.5's derivation shipped as a transform, and itsdirectionargument chooses which way to push. Chapter 16 has the details.The transferable observation is about the thresholds. All three frameworks expose the same parameter under three names:
text Qiskit RemoveIdentityEquivalent(approximation_degree=1.0) Cirq cirq.drop_negligible_operations(atol=1e-08) PennyLane qml.transforms.merge_rotations(atol=1e-08)Qiskit's identity-removal pass takes an
approximation_degreeargument of its own — the same name as §28.7's parameter, doing the same job at a smaller scale. §28.5 measured that pass doing nothing to the wasteful circuit, because at the default nothing crossed its threshold. Loosen it and it starts doing something, and what it starts doing is approximation.So the lesson survives the framework change intact: every one of these tolerances is a fidelity trade you are making without a fidelity measurement. The frameworks differ in how loudly they say so. None of them says it loudly enough.
28.9 A policy
1. ALWAYS use at least level 1. It roughly doubles surviving signal
and costs milliseconds.
2. VERIFY the equivalence claim with Operator.from_circuit, on a small
backend, in your test suite. ~13 ms.
3. DIFF count_ops() before and after. Cheap, and it catches deletions
you did not want.
4. Between levels 2 and 3, prefer 3 -- it won 12 of the 14 trials where
they differed -- but do not expect it to matter on small circuits.
5. DO NOT report a gate-count improvement as a result. Run both circuits
on a noise model and report the fidelity, with a standard error.
6. Barrier anything whose TIMING is the point. Then assert it survived,
because barriers do not protect a removable gate.
7. Touch approximation_degree only with a measured fidelity beside it,
and do the measurement on a backend small enough to allow it.
8. Optimize T count separately, and only when fault tolerance is the
target.
What we measured
- Optimization preserves the unitary exactly at every level — process fidelity
1.000000000000, verified withOperator.from_circuit. - Level 0 → 1 is the big win: surviving signal goes from 6.5% to 12.9% of noiseless. Everything above level 1 is refinement.
- ★ On a Grover-like circuit, level 2 is 15% deeper than level 1 with 9% fewer two-qubit gates — the two standard proxy metrics disagree.
- ★★ Run on a noise model across 8 seeds, L2 − L1 = $+0.0028 \pm 0.0065$ — not significant at 2σ. The 9% gate-count improvement bought nothing measurable.
- ★ Levels 2 and 3 differ in 14 of 40 trials, and level 3 wins 12 of those 14. An earlier draft concluded they were identical from two small circuits that happen to agree.
- On a wasteful test circuit,
CommutativeCancellationalone takes depth 8 → 1, beating the other three passes individually. Pass order changes the output (u1vsrz) though not the unitary. H Htranspiles to depth 0 — annihilated.H barrier Hsurvives. But barriers do not save Chapter 25'sidnoise slot:{'barrier': 2, 'measure': 1}.- ★★
approximation_degree=0.99gives fidelity 0.925, and 0.9 gives ZERO two-qubit gates with fidelity 0.455. It is not a fidelity and it is not gentle. And at 0.9, 0.8 and 0.5 the two-qubit count and depth are identical while fidelity falls 3.87× — the proxy went flat before the thing did. - Level 0's optimization stage contains zero tasks. Recursive task counts by stage: level 0 22, level 1 48, levels 2 and 3 54 each — and the layout stage is not monotone (level 1 runs 9, levels 2 and 3 run 7). Introspecting non-recursively returns six entries, four of which are controllers.
- Transpile time, best of three: level 3 costs 1.4× level 1 at 5, 8 and 11 qubits and 1.8× at 14 — 0.020 s in absolute terms. Cost is not a reason to choose a lower level.
- Derived from the measured spread, not separately measured: resolving $+0.0028$ at $2\sigma$ needs 173 seeds (21.6× what we ran, 6,920,000 shots); the 8-seed study instead bounds the effect below 0.0130. The null result prices the trade at 1 two-qubit gate ≈ 5.5 layers of depth, and a gate-count-only model predicts $+0.0197$ — 2.6σ above what was measured.
The theme: every optimization is a claim of equivalence, and every optimization metric is a proxy — so verify the claim exactly, and measure the proxy against the thing you actually care about.