34 min read

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.

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 — DoWhileController and ConditionalController — 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.

TwoQubitPeepholeOptimization is the pass that replaced the older Collect2qBlocksConsolidateBlocksUnitarySynthesis chain 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.

RemoveIdentityEquivalent is the current name; RemoveIdentityEquivalentGates does 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].tasks path used in a lot of older code still works and is not what you should write; pm.optimization.to_flow_controller().tasks is the supported entry point, and it is the one that hands you the controllers described above.

The legacy QFT and EfficientSU2 classes also still import in 2.5.1 alongside the QFTGate / efficient_su2 forms 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 ecr error $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.2239

Level 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 ns

Chapter 39 also measured the gates themselves: cz 68–184 ns, sx 32–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.0065

The 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.00729

against 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 circuitwhich 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 CX as 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 RZ on 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 CX reads 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 RZ on 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 rz on 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 rz gates 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 FakeSherbrooke is built the way it is. rz is free, sx is a real pulse, ecr is an expensive real pulse, and every single-qubit optimization in the transpiler is ultimately a scheme for pushing work out of sx and into rz. Optimize1qGatesDecomposition rewriting 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 id gate. Assert the gate count after transpilation — Chapter 26 §26.5's count_ops diff, 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 delay survive? 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 X dynamical-decoupling pair inside barriers survive? It is the same $UU^\dagger = I$ shape as H 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_degree is not a gentle dial, and it is not a fidelity.

0.99 does 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.0406

Now 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's Optimize1qGatesDecomposition), cirq.drop_negligible_operations (Qiskit's RemoveIdentityEquivalent), then cirq.drop_empty_moments and cirq.align_left or cirq.stratified_circuit for the scheduling. Chapter 14 has the details.

PennyLane 0.45.1 is the same shape: qml.compile(pipeline=[...]) over qml.transforms.cancel_inverses, merge_rotations, commute_controlled, single_qubit_fusion, undo_swaps, remove_barrier, pattern_matching_optimization. Note the name commute_controlled — it is §28.5's derivation shipped as a transform, and its direction argument 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_degree argument 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 with Operator.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, CommutativeCancellation alone takes depth 8 → 1, beating the other three passes individually. Pass order changes the output (u1 vs rz) though not the unitary.
  • H H transpiles to depth 0 — annihilated. H barrier H survives. But barriers do not save Chapter 25's id noise slot: {'barrier': 2, 'measure': 1}.
  • ★★ approximation_degree=0.99 gives 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.