> *"Mitigation buys accuracy with shots. The exchange rate is terrible, and it is the only exchange
Prerequisites
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
Learning Objectives
- Distinguish error mitigation from error correction, and state what each can and cannot do.
- Build, invert, and apply a measurement assignment matrix.
- Recognize when readout mitigation helps, when it makes things worse, and when it is impossible.
- Apply zero-noise extrapolation by gate folding, and explain what noise it cannot remove.
- Configure dynamical decoupling, and verify whether it is actually doing anything.
- Order mitigation techniques so that they compound rather than compete.
- Account honestly for the sampling overhead each technique costs.
In This Chapter
- Overview
- Learning Paths
- 13.1 Mitigation Is Not Correction
- 13.2 Readout Mitigation: The Assignment Matrix
- 13.3 When Readout Mitigation Fails
- 13.4 The Runtime's Built-In Mitigation
- 13.5 Zero-Noise Extrapolation
- 13.6 Dynamical Decoupling
- 13.7 Ordering: Making Techniques Compound
- 13.8 Probabilistic Error Cancellation and Twirling
- 13.9 What Each Technique Costs
- 13.10 Summary
Chapter 13: Error Mitigation
"Mitigation buys accuracy with shots. The exchange rate is terrible, and it is the only exchange currently open."
Overview
Chapter 11 measured the noise. Chapter 12 routed around it. This chapter fights back.
Error mitigation is a family of techniques that recover an accurate expectation value from noisy runs by running more circuits and doing classical post-processing. It does not make your circuit noiseless. It does not scale to arbitrary depth. It is what we have until error correction arrives (Chapter 25), and used carefully it is worth a great deal.
The chapter's central measurement, on a four-qubit GHZ state where the true $\langle ZZZZ\rangle$ is exactly $+1$:
| unmitigated | with ZNE | |
|---|---|---|
| raw | error 0.08228 | error 0.07050 (−14%) |
| readout-mitigated | error 0.02954 (−64%) | error 0.01709 (−79%) |
Three things fall out of that table, and they organize the chapter.
Readout mitigation alone removed 64% of the error for sixteen extra circuits. ZNE alone removed 14% for twelve times the two-qubit gate count. On this circuit readout mitigation is roughly 4.5× more effective and vastly cheaper, confirming what Chapter 11 §11.7 predicted from the noise signatures.
And the two compound. ZNE removes 14% of the raw error but 42% of the post-readout-mitigation error — three times more effective when it runs second. §13.7 explains exactly why, and the reason tells you how to order any mitigation stack.
You will also see all three techniques fail, which is the more useful half of the chapter:
- Readout mitigation made a bad-qubit result worse (L1 error 1.0305 → 1.0654).
- On a stuck qubit the assignment matrix is singular — mitigation is not merely ineffective but mathematically impossible.
- Dynamical decoupling in its default configuration did nothing at all, and when forced to engage made the result worse by 6 percentage points.
In this chapter, you will learn to:
- Separate mitigation from correction.
- Build and invert an assignment matrix, and know when not to.
- Apply ZNE by gate folding, and identify the noise it cannot touch.
- Configure dynamical decoupling and verify it is running.
- Order techniques so they compound.
- Price each technique in shots.
Learning Paths
How to read this chapter by track. - 🔰 Beginner — §13.1, §13.2, and §13.9. Readout mitigation is the one technique everyone should use. - 🔬 Researcher — §13.3 and §13.10. What you must disclose when you report mitigated results. - 🤖 Quantum ML — §13.7 and §13.8; mitigation cost multiplies by every training iteration. - 🏗️ Quantum Engineer — all of it, especially §13.6's verification that DD is actually running. - 🔐 Security — skim, then Chapter 25.
13.1 Mitigation Is Not Correction
The distinction is the most important idea in the chapter, and it is routinely blurred.
| Error mitigation | Error correction | |
|---|---|---|
| What it fixes | the expectation value | the quantum state |
| Qubit overhead | none | 100–1000× physical per logical |
| Shot overhead | large and growing with depth | modest |
| Scales to deep circuits? | no | yes, that is the point |
| Available today | yes | not at useful scale |
| Gives you a correct single shot? | no | yes |
Mitigation never repairs a run. It takes many noisy runs and computes a better estimate of an average. If your algorithm needs a single correct bitstring — Shor's factoring, Grover's search — most mitigation techniques do nothing for you at all. If your algorithm needs an expectation value — VQE, QAOA, anything variational — mitigation is the difference between a usable result and noise.
And the overhead grows with circuit depth. As noise increases, the number of shots needed to resolve the mitigated value through the added variance grows too, eventually exponentially. Mitigation does not defer the exponential wall; it buys you a constant factor of room in front of it. A valuable constant factor, and a constant factor.
Bias, variance, and which one shots can buy
The table above is a list of facts. The reason behind all of them is one line of statistics.
What you get back from $N$ shots is an average, $\hat\mu = \frac{1}{N}\sum_i o_i$, and its distance from the truth decomposes into two terms that behave completely differently:
$$\mathbb{E}\big[(\hat\mu - \mu_{\text{ideal}})^2\big] \;=\; \underbrace{\text{bias}^2}_{\text{noise}} \;+\; \underbrace{\sigma^2/N}_{\text{sampling}}$$
Only the second term contains $N$. Running more shots shrinks the sampling term as $1/\sqrt{N}$ — Chapter 27 §27.5 measured that scaling directly on a perfectly correct GHZ(3), 40 runs per shot count: mean TVD 0.04375 at 100 shots, 0.01313 at 1,000, 0.00423 at 10,000, tracking $1/\sqrt{N}$ the whole way. The bias term does not move. It is the same displacement in every shot, so averaging preserves it perfectly.
This chapter's 0.08228 error on $\langle ZZZZ\rangle$ is bias. The noise pushes the estimate the same direction every time. That is why the chapter's opening table can report an error to five decimal places from a single 16,384-shot run and mean it: the number is stable, and it is stably wrong.
Mitigation is a machine for converting bias into variance. It runs extra circuits, combines them with signed classical weights, and produces an estimator whose bias is smaller and whose variance is larger. Correction does something categorically different: it spends qubits to repair the state, so the noisy run never happens and there is no bias to convert. Chapter 15 priced that currency — a single $T$ gate went from 450 to 2,882 physical qubits, a 6.4× cliff for one gate.
Put the exchange rate in numbers, using this chapter's own budget. The full stack cost 16 calibration circuits at 8,192 shots plus four scale factors at 16,384:
unmitigated baseline 16,384 shots
readout calibration 16 x 8,192 131,072
ZNE 4 scales x 16,384 65,536
total 196,608 shots 12.00x
error 0.08228 -> 0.01709 4.82x better
Twelve times the shots for 4.82× the accuracy. Now price the same accuracy improvement the only other way available — brute force. Cutting a $1/\sqrt{N}$ error by 4.82× costs $4.82^2 = 23.2\times$ the shots, nearly twice what mitigation charged.
And brute force would have bought exactly none of it, because the error is bias and $N$ does not appear in the bias term. That is the whole argument for mitigation in one comparison: it is expensive, it is cheaper than the alternative, and the alternative does not work.
📐 Math Aside — Why the overhead must grow exponentially, and why the base is greater than one.
The exponential in §13.1's table is not an engineering shortfall that better hardware will retire. It follows from what a noise channel is.
A noise channel $\mathcal{E}$ is completely positive and trace preserving, and it contracts the set of physical states — that is what it means to lose information. Its inverse therefore expands that set, mapping some physical states outside it. $\mathcal{E}^{-1}$ is not a physical operation, so no device can apply it directly.
What you can do is write it as a signed combination of operations the device does implement:
$$\mathcal{E}^{-1} \;=\; \sum_i c_i\,\mathcal{O}_i, \qquad \gamma \;\equiv\; \sum_i |c_i|$$
Trace preservation forces $\sum_i c_i = 1$. Non-physicality forces at least one $c_i < 0$. Those two facts together force
$$\gamma \;=\; \sum_i |c_i| \;>\; \sum_i c_i \;=\; 1$$
$\gamma > 1$ is a theorem, not a specification. Sample $\mathcal{O}_i$ with probability $|c_i|/\gamma$ and weight each outcome by $\gamma\,\mathrm{sign}(c_i)$; the estimator is unbiased and its magnitude is $\gamma$ times larger, so its variance is roughly $\gamma^2$ times larger.
Now compose $d$ noisy layers. The inverses multiply, so the norms multiply: $\gamma_{\text{tot}} = \gamma^d$, variance $\gamma^{2d}$, and
$$N \;\propto\; \frac{\gamma^{2d}}{\epsilon^2}$$
The depth is in the exponent. Correction's overhead is a constant factor per logical qubit — large, but a constant. Mitigation's is an exponential in depth. This is the book's recurring observation that every remedy is denominated in the currency of the disease, and the two remedies here are denominated differently: one in qubits, one in an exponent.
🔬 Honest Assessment — What mitigation is actually for.
There is a persistent framing in which mitigation is a bridge: it gets us useful quantum computing now, before error correction is ready. That framing oversells it.
What mitigation genuinely does: it makes near-term expectation-value experiments quantitatively meaningful instead of qualitatively suggestive. Getting $\langle ZZZZ\rangle$ from 0.918 to 0.983 when the answer is 1.0 turns "the sign is right" into "the value is right to two percent." For benchmarking, for validating a device, and for small variational problems, that is a real and important capability.
What it does not do: enable a computation that would otherwise be out of reach. The circuits that mitigation rescues are circuits a classical computer can usually also simulate. As depth grows, the shot cost grows faster than the benefit, and there is no depth at which mitigation alone crosses into classically-hard territory.
Use it. Report it honestly. Do not describe it as a path to quantum advantage.
13.2 Readout Mitigation: The Assignment Matrix
Chapter 11 §11.7 found readout error produces the largest histogram distortion per unit of underlying error rate — roughly $2p$ against depolarizing's $p/2$. Chapter 12 measured it directly: on a healthy two-qubit pair, readout accounts for 51.5% of a Bell state's total error; on a bad pair, 72.4%.
It is also the easiest error to undo, because measurement error is classical. It acts after the quantum state is gone, so it is described by an ordinary stochastic matrix.
The idea. For $n$ qubits, prepare each of the $2^n$ computational basis states, measure it, and record where the shots actually landed. That gives a matrix $A$ where $A_{ij}$ is the probability of observing $i$ when the true state was $j$:
$$\mathbf{p}_{\text{observed}} = A\,\mathbf{p}_{\text{true}} \qquad\Longrightarrow\qquad \mathbf{p}_{\text{true}} = A^{-1}\mathbf{p}_{\text{observed}}$$
Building it takes $2^n$ circuits with no gates but the state preparation:
order = ["00", "01", "10", "11"]
cal = []
for bits in order:
qc = QuantumCircuit(2, 2)
for i, ch in enumerate(reversed(bits)): # little-endian: bit i -> qubit i
if ch == "1":
qc.x(i)
qc.measure([0, 1], [0, 1])
cal.append(pm.run(qc))
Measured on qubits [122, 123], 8192 shots each:
assignment matrix A (columns = prepared, rows = observed)
00 01 10 11
00 0.9897 0.0098 0.0115 0.0001
01 0.0046 0.9854 0.0001 0.0090
10 0.0056 0.0001 0.9851 0.0109
11 0.0000 0.0048 0.0033 0.9800
condition number: 1.03
Read the structure. The diagonal is ~0.985 — the measurement is usually right. The large off-diagonal entries are single-bit flips (0.0098, 0.0115, 0.0046, …); the double-flip corners are ~0.0001, a hundred times smaller, because two simultaneous readout errors are rare. The matrix is strongly diagonally dominant, which is why the condition number is 1.03 and the inversion is safe.
Applying it:
raw = np.array([counts.get(k, 0) / shots for k in order])
mitigated = np.linalg.solve(A, raw) # solve, don't invert
On a Bell state:
raw {'00': 0.4926, '01': 0.0109, '10': 0.0090, '11': 0.4875}
mitigated {'00': 0.4975, '01': 0.0041, '10': 0.0008, '11': 0.4975}
ideal {'00': 0.5, '01': 0.0, '10': 0.0, '11': 0.5}
L1 error: 0.0398 -> 0.0099 a 4x reduction
Four times better, from sixteen thousand extra shots and a linear solve. The two impossible outcomes drop by an order of magnitude and the peaks move to within 0.25% of a half.
What the inversion costs in variance
§13.1 said mitigation converts bias into variance. The assignment matrix is the one place in this chapter where you can compute both sides of that trade exactly, because the matrix is printed above.
Any expectation value is a linear functional of the distribution. For $\langle ZZ\rangle$ the weights are the parity signs $\mathbf{w} = (+1,-1,-1,+1)$, and the mitigated estimate is
$$\mathbf{w}^{\mathsf T} A^{-1}\hat{\mathbf p} \;=\; \big(A^{-\mathsf T}\mathbf{w}\big)^{\mathsf T}\hat{\mathbf p}$$
So mitigation does not change what you measure. It changes the weights you apply to it. And because $\hat{\mathbf p}$ is a multinomial average with $\operatorname{Cov}(\hat{\mathbf p}) = (\operatorname{diag}(\mathbf p) - \mathbf{p}\mathbf{p}^{\mathsf T})/N$, the variance penalty is completely determined by how much larger the new weights are.
Inverting the published matrix gives them:
parity weights raw +1.00000 -1.00000 -1.00000 +1.00000
parity weights A^-T w +1.02100 -1.02990 -1.03040 +1.04120
Every weight grew, and none grew by much. Carrying that through to the per-shot variance of $\langle ZZ\rangle$ on the Bell distribution this section measured:
variance per shot, raw 0.059341
variance per shot, mitigated 0.063134
ratio 1.0639
Readout mitigation costs 6.4% more shots to hold the same standard error. Not 4×, not 2× — six percent. Against that it removes a bias of 0.030: applying the published $A$ to a perfect Bell distribution predicts a raw $\langle ZZ\rangle$ of $+0.96985$ rather than $+1$, and the inversion returns $+1.00000$ exactly.
Six percent of variance for three points of bias is the best exchange rate in this chapter, and it is why §13.9 says "always."
The general rule falls out of the same algebra: the variance penalty is governed by how much $A^{-\mathsf T}$ stretches the weight vector, which is bounded by the condition number. Read the condition number as a shot tax. At $\mathrm{cond}(A) = 1.03$ the tax is a few percent. At the bad pair's 2.05 it is a real cost — and §13.3 shows the bias it removes there is not the bias you have.
📊 What the Numbers Say — two separate experiments agree on the per-qubit readout floor.
The matrix above came from
example-01, on qubits [122, 123]. §13.5's gate-free probe came fromexample-03, on qubits [122, 123, 124, 125]. Different scripts, different qubit counts, different observables. They should agree on the underlying per-qubit readout quality, and they do.From the matrix, column
00weighted by parity: $0.9897 - 0.0046 - 0.0056 + 0.0000 = 0.97950$, so the per-qubit $\langle Z\rangle$ is $\sqrt{0.97950} = 0.98970$.From the gate-free four-qubit probe: $\langle ZZZZ\rangle = 0.96228$, so the per-qubit value is $0.96228^{1/4} = 0.99043$.
text per-qubit <Z> from the 2-qubit assignment matrix 0.98970 per-qubit <Z> from the 4-qubit gate-free probe 0.99043 difference 0.00073Writing $\langle Z\rangle = 1 - 2\varepsilon$ gives $\varepsilon = 0.00478$ per qubit, and $(1-2\varepsilon)^4 = 0.96228$ closes the loop exactly.
Neither number was checked against the other when it was measured. Agreement to $7\times10^{-4}$ across two experiments is the kind of consistency that earns a measurement the right to be quoted — and the honest caveat is that the qubit sets overlap rather than match, and each figure is a single run.
⚠️ Common Pitfall — Use
solve, notinv.
np.linalg.solve(A, raw)is more accurate and faster thannp.linalg.inv(A) @ raw. It matters more as $A$ gets larger and worse-conditioned — which is exactly the regime where you are already in trouble.And always check the condition number before trusting the result. A condition number of 1.03 means errors are barely amplified. §13.3 shows what happens when it is not.
13.3 When Readout Mitigation Fails
Three failure modes, in increasing order of severity. All three were measured on the same device.
Same Bell circuit, three qubit pairs:
pair cond L1 raw L1 mitigated
GOOD [122,123] 1.03 0.0398 -> 0.0099 IMPROVED (4.0x)
BAD [9,8] 2.05 1.0305 -> 1.0654 WORSE
STUCK [84,83] 3.7e33 SINGULAR MATRIX
Failure 1: it can make things worse
On qubits [9, 8], mitigation increased the error. The reason is not subtle once you look at where the error came from: the pair (9,8) has an ECR gate error of 1.0 — a dead link, from Chapter 12's list. The state was destroyed by the entangling gate, not by the measurement.
Readout mitigation only removes readout error. Inverting the assignment matrix redistributes probability according to a model of measurement error; when the dominant error is elsewhere, the inversion is applying a correction for a problem that is not the problem, and the amplified statistical noise is all you get.
Mitigation is not a general-purpose accuracy improver. Each technique targets one channel, and applying it to a different channel costs you shots and buys you nothing.
Failure 2: it can be mathematically impossible
Qubit 84 is Chapter 12's stuck qubit. Its assignment matrix:
00 01 10 11
00 0.0000 0.0000 0.0000 0.0000
01 0.9685 0.9666 0.0159 0.0131
10 0.0000 0.0000 0.0000 0.0000
11 0.0315 0.0334 0.9841 0.9869
determinant 0.000e+00 rank 2 (should be 4)
Two rows are identically zero — the outcomes 00 and 10 never occur, because qubit 84 always
reports 1. And the first two columns are nearly identical: preparing $|00\rangle$ and preparing
$|01\rangle$ produce indistinguishable output distributions, because the bit that differs between them
is the stuck one.
The matrix has rank 2 where it should have rank 4 — exactly one qubit's worth of information missing, which is precisely correct, since one of the two qubits reports nothing.
$$\text{rank}(A) = 2 \quad\Longrightarrow\quad A^{-1} \text{ does not exist}$$
📐 Math Aside — Why rank deficiency is the right way to say "hopeless."
A singular assignment matrix is not a numerical inconvenience. It is a statement that the measurement is a non-injective map: two distinct input states produce the same output distribution, so no post-processing whatsoever can distinguish them.
You cannot invert information that was destroyed. Mitigation post-processes a distribution; it cannot recover a distinction the hardware never recorded. The rank deficit counts the qubits whose information was lost — here, one.
This is the sharpest available answer to "can mitigation fix my bad qubit?" Compute the rank. If it is deficient, the answer is no, and no amount of shots changes it.
Failure 3: it does not scale
$2^n$ calibration circuits:
| qubits | calibration circuits | matrix to invert |
|---|---|---|
| 2 | 4 | 4 × 4 |
| 6 | 64 | 64 × 64 |
| 10 | 1,024 | 1024 × 1024 |
| 20 | 1,048,576 | 10⁶ × 10⁶ |
| 50 | 1.1 × 10¹⁵ | hopeless |
Beyond about ten qubits the full matrix is out of reach, and the exponential is in the calibration, not the circuit.
Where the dense method actually dies
The circuit count is the exponential everyone quotes. It is not the one that kills you first.
Storing $A$ densely takes $8 \cdot 4^n$ bytes. Factorizing it takes $O\big((2^n)^3\big) = O(8^n)$ flops — every qubit you add multiplies the solve by eight.
n matrix dense bytes LU flops time at 1e12 flop/s
2 4x4 128 6.4e1 instant
10 1,024^2 8.39e+06 1.07e+09 ~1 ms
20 1,048,576^2 8.80e+12 1.15e+18 13.3 days
30 1.07e+09 ^2 9.22e+18 1.24e+27 —
50 1.13e+15 ^2 1.01e+31 1.43e+45 —
Ten qubits is comfortable and twenty is gone. At $n = 10$ the matrix is 8 MB and the solve is a millisecond — you would never notice it. At $n = 20$ you must first run 1,048,576 calibration circuits, then hold 8.8 terabytes in memory, then wait thirteen days on the linear algebra. At $n = 30$ the matrix alone is 9.2 exabytes.
There is no clever pivoting or sparse storage that fixes this, because $A$ is not sparse. Every entry is a genuine probability, and the ones that are small are small, not zero.
The practical answers:
Tensored mitigation assumes readout errors are independent across qubits, so the full matrix factors into $n$ independent $2\times2$ matrices. Cost drops from $2^n$ to 2 calibration circuits. It ignores correlated readout error, which is real but usually small.
M3 (matrix-free measurement mitigation) never forms the full matrix. It works only in the subspace
of bitstrings you actually observed — typically thousands, not $2^{50}$ — and solves iteratively.
This is what the runtime's built-in measure_mitigation uses, and it is what you should use in
practice.
Why "never forms the matrix" is the whole idea
M3 avoids the exponential three separate times, and it is worth separating them because each one removes a different exponential.
1. The problem is only as wide as your data. The matrix is $2^n$ wide, but $N$ shots can produce at most $N$ distinct bitstrings. So the subspace that carries any of your probability has dimension at most $\min(2^n, N)$ — and at this chapter's 16,384 shots that is at most 16,384, for any $n$ whatsoever.
$$\dim \;\le\; \min\!\big(2^n,\; N_{\text{shots}}\big)$$
The size of the problem is set by the shot count, not the qubit count. That single observation converts a $2^{50}$ into a 16,384, and everything else follows from it. In practice the count is far lower still: a GHZ state concentrates on two outcomes, a converged variational ansatz on a handful.
2. The entries are computed, not stored. Under a per-qubit (tensored) calibration, $A_{ij}$ is a product of $n$ two-by-two factors — so any entry can be produced on demand in $O(n)$ arithmetic instead of read from an array of $4^n$ numbers. The "matrix" becomes a function. Calibration drops from $2^n$ circuits to a small constant.
3. The solve is iterative. Krylov methods need only matrix–vector products, never a factorization. That is what removes the $8^n$ — and the $K^3$ of the reduced problem too.
🔬 Honest Assessment — M3 buys scalability with two assumptions, and you should know both.
The entry model assumes readout errors are independent across qubits. The published matrix lets you test that. Under independence, the double-flip corner should be the product of the two single-flip rates: $0.0098 \times 0.0115 / 0.9897 = 1.14\times10^{-4}$, against a measured $1\times10^{-4}$. Consistent — but the measured entry carries one significant figure, so this confirms the order of magnitude and nothing finer.
The diagonal tells a slightly different story. Assignment fidelity falls 0.9897 → 0.9854 / 0.9851 → 0.9800 as qubits are excited: about 0.0043 and 0.0046 for one, and 0.0097 for both against the 0.0089 that additivity predicts. Small, real, and in the direction of positive correlation.
The subspace restriction assumes probability outside the observed set is negligible. Usually true; not a theorem.
Neither approximation is a reason to avoid M3 — the dense alternative does not exist above ~15 qubits, so the comparison is not "approximate versus exact," it is "approximate versus nothing." But a tensored model cannot represent correlated readout error by construction, and correlated readout error is exactly what a crosstalk problem looks like.
Build the matrix by hand once, to understand it. Then use M3.
13.4 The Runtime's Built-In Mitigation
EstimatorV2 exposes mitigation through options rather than requiring you to build any of this:
estimator = EstimatorV2(mode=backend)
estimator.options.resilience_level = 1 # coarse dial: 0, 1, 2
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.noise_factors = (1, 3, 5)
estimator.options.resilience.zne.extrapolator = "exponential"
estimator.options.twirling.enable_gates = True
estimator.options.dynamical_decoupling.enable = True
estimator.options.dynamical_decoupling.sequence_type = "XpXm"
resilience_level |
What it turns on | Cost |
|---|---|---|
| 0 | nothing | 1× |
| 1 | readout mitigation (+ twirling) | ~1× plus calibration |
| 2 | adds ZNE | several × |
★ The default is not "none"
The single most consequential fact about that table is which row you get when you set nothing at all.
From EstimatorOptions in qiskit-ibm-runtime 0.48.0, on the resilience_level field:
Default: 1.
Not zero. One. Every option in the block is a sentinel — reading estimator.options.resilience_level
back on your own machine returns Unset, because the resolution happens server-side — so there is no
local inspection that will tell you what you are about to run. What the server fills in when each field
is left Unset:
option level 0 level 1 level 2
resilience.measure_mitigation False True True
resilience.zne_mitigation False False True
resilience.pec_mitigation False False False
twirling.enable_measure False True True
twirling.enable_gates False False True
An EstimatorV2 expectation value you did not configure is a readout-mitigated,
measurement-twirled number. It is not raw hardware. Describing it as raw hardware — in a paper, in a
benchmark, in a comparison against a classical baseline — is a straightforward misstatement of what was
run.
Three details from the same source that are easy to get wrong:
MAX_RESILIENCE_LEVEL = 2. A validator rejects anything outside $[0, 2]$; there is no level 3.SamplerV2defaults the other way. Both twirling flags default toFalsefor the Sampler. The two primitives ship with opposite mitigation defaults, so the same circuit run through each is not the same experiment.- ZNE and PEC are mutually exclusive. Setting both raises:
'pec_mitigation' and 'zne_mitigation' options cannot be simultaneously enabled.They are alternatives, not a stack.
Note also what level 1's "+ twirling" means precisely: measurement twirling only. Gate twirling
arrives at level 2, which is consistent with §13.8's account of why twirling.enable_gates accompanies
ZNE — ZNE needs the noise to match the model it assumes, and measurement twirling does not deliver
that.
⚠️ Common Pitfall — "Unmitigated" is a configuration you must request, not a default you receive.
If your experiment needs a genuinely raw baseline — a noise characterization, a before-and-after comparison, anything in Chapter 30's benchmarking chapter — you must write
estimator.options.resilience_level = 0explicitly.And note that the dial is coarse: level 0 switches off measurement twirling along with readout mitigation, so "raw" and "twirled" are not separable by the level alone. Set the individual
resilience.*andtwirling.*fields when you need one and not the other.The reporting rule follows directly. A quoted expectation value is not reproducible without its resilience level, because the level is not zero and cannot be inferred from the code that produced it. §13.9 step 7 says "report what you used"; this is why it is a step and not a courtesy.
🗝️ Version Note —
resilience_leveldoes nothing on a fake backend.Running levels 0, 1, and 2 against
FakeSherbrookereturns bit-identical results:
text level 0: <ZZ> = +0.96387 std 0.00294 level 1: <ZZ> = +0.96387 std 0.00294 level 2: <ZZ> = +0.96387 std 0.00294Qiskit does warn you, if you are reading warnings:
text UserWarning: The resilience_level option has no effect in local testing mode.The resilience options are implemented server-side, so local testing mode silently ignores them. This is the one part of the hardware workflow the fake-backend path cannot rehearse.
The consequence for this chapter is a happy one: every technique here is built by hand, which is both verifiable locally and a much better way to learn what the options actually do. Verified against qiskit-ibm-runtime 0.48.0.
13.5 Zero-Noise Extrapolation
If you cannot remove the noise, measure at several known noise levels and extrapolate to zero.
Amplifying noise on purpose. Since noise scales with the number of gates, replace each gate — or the whole circuit — with an equivalent that uses more of them. Global folding maps
$$U \;\longrightarrow\; U (U^\dagger U)^k$$
which is mathematically the identity operation on $U$, and multiplies the gate count by $2k+1$.
def fold(circuit, factor):
"""Global folding. factor must be odd."""
k = (factor - 1) // 2
out = circuit.copy()
for _ in range(k):
out = out.compose(circuit.inverse()).compose(circuit)
return out
⚠️ Common Pitfall — Fold the logical circuit, then transpile. Never fold an ISA circuit.
isa_circuit.inverse()produces gates likesxdgthat are not in the device basis, and the primitive rejects the result:
text IBMInputValueError: The instruction sxdg on qubits (125,) is not supported by the target system.Fold first, transpile each folded circuit second — and pass the same
initial_layoutto every one, so you are comparing noise levels rather than layouts.
Measured, four-qubit GHZ, $\langle ZZZZ\rangle$, true value $+1$:
scale ecr gates depth <ZZZZ>
1 3 13 +0.91772
3 7 25 +0.89392
5 11 37 +0.87012
7 15 49 +0.84668
A clean, nearly linear decay — which is what makes extrapolation viable.
unmitigated (scale 1) +0.91772 error 0.08228
linear extrapolation +0.92950 error 0.07050
quadratic extrapolation +0.92975 error 0.07025
exponential extrapolation +0.93039 error 0.06961
All three extrapolators agree to within 0.001 — reassuring, since disagreement among extrapolators is the standard warning sign that you are extrapolating past what the data supports.
Deriving the extrapolation, and naming the assumption
The three extrapolators are not arbitrary curve fits. Two of them are the exactly-correct model under two different noise assumptions, and knowing which is which tells you what agreement means.
Model each gate as applying depolarizing noise of strength $p$, independently. A circuit of $G$ gates then preserves a fraction $(1-p)^G$ of the ideal expectation value:
$$\langle O\rangle \;=\; (1-p)^{G}\,\langle O\rangle_{\text{ideal}}$$
Fold to $\lambda G$ gates and the exponent scales with $\lambda$:
$$\langle O\rangle(\lambda) \;=\; (1-p)^{\lambda G}\langle O\rangle_{\text{ideal}} \;=\; e^{\lambda G \ln(1-p)}\,\langle O\rangle_{\text{ideal}}$$
Exponential in $\lambda$, exactly. So the exponential extrapolator is model-correct under global depolarizing noise, and its $\lambda \to 0$ limit returns $\langle O\rangle_{\text{ideal}}$ with no approximation at all. Expand for $\lambda G p \ll 1$:
$$\langle O\rangle(\lambda) \;\approx\; \big(1 - \lambda G p\big)\langle O\rangle_{\text{ideal}}$$
Linear in $\lambda$ — which is why the linear extrapolator works, and why it stops working when $Gp$ is no longer small.
Now test both models against the chapter's own four points. A linear model predicts constant differences; an exponential model predicts constant ratios:
scale <ZZZZ> difference ratio
1 +0.91772 -- --
3 +0.89392 -0.02380 0.974066
5 +0.87012 -0.02380 0.973376
7 +0.84668 -0.02344 0.973061
Both hold. The first two differences are identical to five decimal places; the ratios drift by 0.001 across the whole range. Four points spanning $\langle ZZZZ\rangle$ from 0.847 to 0.918 simply cannot distinguish a line from an exponential — over that interval they are the same curve.
That reframes the agreement. The 0.0009 spread is not evidence that the extrapolation is right; it is a measurement of how little the data constrains the model. Least squares on the four points, by hand, reproduces every published figure:
slope -0.011846, intercept +0.929494 published linear +0.92950
quadratic +0.929742 published quadratic +0.92975
exponential +0.930390 published exponential +0.93039
The extrapolation is arithmetic, and the arithmetic is correct. What it rests on is an assumption that no amount of extrapolator agreement can test:
ZNE assumes the noise scales the way the folding models it — that inserting $U^\dagger U$ multiplies the effective noise parameter by $\lambda$ and leaves its character unchanged.
That assumption fails in two distinct ways on this very circuit. The next two subsections are both of them.
And the honest reading: ZNE removed about 15% of the error at twelve times the two-qubit gate cost (36 gates executed against 3). That is a poor trade, and §13.7 shows it is poor for a specific, fixable reason.
The scale factor is not the number you passed in
Look again at the gate counts in the measured table. They contradict the scale factors sitting next to them.
GHZ(4) has three cx gates. Folding to $\lambda = 3, 5, 7$ should give 9, 15, and 21. The transpiled
circuits carry 7, 11, and 15:
lambda nominal 3*lambda measured ecr cancelled
1 3 3 0
3 9 7 2
5 15 11 4
7 21 15 6
Each fold adds four two-qubit gates, not six. The mechanism is visible in the folding identity.
$U$ ends with cx(2,3); $U^\dagger$ begins with cx(2,3), and cx is self-inverse, so the pair
annihilates. Every added $(U^\dagger U)$ block introduces exactly one such junction and loses exactly
two ecr gates. The other junction is an $h$ pair, which costs no two-qubit gates.
So the true noise scale factors — measured in the thing that actually carries the noise — are
$$\lambda_{\text{eff}} \;=\; \frac{3,\,7,\,11,\,15}{3} \;=\; 1,\;2.333,\;3.667,\;5.000$$
At nominal $\lambda = 7$ the circuit carries five times the two-qubit gates, not seven. The fit was performed against an $x$-axis that overstates the noise by up to 40%, which tilts the line and drags the intercept down.
Refit on the measured axis, same four values, no new shots:
fit against intercept error vs 1.0
lambda = 1,3,5,7 +0.92950 0.07050
ecr/3 = 1,2.33,3.67,5 +0.93542 0.06458
An 8.4% improvement in the extrapolated error, for free. Not because a better extrapolator was used — the same linear fit — but because the independent variable was corrected to something that was measured rather than assumed.
The rule: fit against a quantity you counted, not the argument you passed to fold(). Count the
two-qubit gates in each transpiled circuit and use those as the abscissa.
The honest limit of the fix: 0.93542 is still 0.02686 short of the gate-free 0.96228 that §13.5's next subsection measures, so the affine decomposition is a first-order account and not an identity. Single-qubit gates and idle-time decoherence scale with depth (13, 25, 37, 49), which is a third axis again; refitting against depth lands at 0.94331. Three defensible $x$-axes give three different intercepts spanning 0.014. That spread — not the 0.0009 among extrapolators — is the honest uncertainty on this extrapolation.
⚙️ Under the Transpiler — ★★ At
optimization_level=2, ZNE silently does nothing.$U(U^\dagger U)^k = U$ is an algebraic identity. A sufficiently good circuit optimizer will notice.
Rerunning
example-03's measurement at each optimization level, same layout, same seed, same 16,384 shots:```text optimization_level = 1 optimization_level = 2 scale ecr depth
scale ecr depth 1 3 13 +0.91772 1 3 17 +0.91724 3 7 25 +0.89392 3 3 17 +0.91724 5 11 37 +0.87012 5 3 17 +0.91724 7 15 49 +0.84668 7 3 17 +0.91724 linear extrapolation +0.92950 linear extrapolation +0.91724 spread across scales 0.07104 spread across scales 0.00000 ZNE improvement +14.3% ZNE improvement -0.0% ```
Four bit-identical values. Zero spread. The extrapolation returns the unmitigated number. The level-1 arm reproduces the chapter's published table exactly, which is the control that makes the level-2 arm readable.
Gate counts across all four levels, checked in six fresh processes and identical every time:
text level 0: ecr 3, 9, 15, 21 folding preserved exactly level 1: ecr 3, 7, 11, 15 partially cancelled -- the published table level 2: ecr 3, 3, 3, 3 folding deleted level 3: ecr 3, 3, 3, 3 folding deletedLevel 1 is an accidental middle ground: one adjacent-inverse sweep removes the outermost pair at each junction and stops. Levels 2 and 3 iterate until the identity collapses.
Nothing errors. Nothing warns. ZNE runs, fits a flat line through four identical points, and reports an intercept with an air of authority. This is §13.6's dynamical-decoupling failure in a new costume, and Chapter 11's testing trap in a third: a technique that never reaches the case it is meant to handle proves nothing, and reports success.
Chapter 28 measured the surrounding behaviour — optimization levels 2 and 3 differing in 14 of 40 circuit-seed pairs, and
approximation_degree=0.9producing zero two-qubit gates. The compiler deleting the thing you meant to measure is documented, not exotic.The check, and assert on it before fitting:
python counts = [isa.count_ops().get("ecr", 0) for isa in folded] assert all(b > a for a, b in zip(counts, counts[1:])), f"folding collapsed: {counts}"Chapter 31 §31.4 warns against asserting on the free gates, whose counts are nondeterministic even with a fixed
seed_transpiler.ecris precisely the one that is stable — and it is the one that matters here.
Why ZNE stalled
Extrapolating to "zero noise" gave 0.930, not 1.000. Something is not being scaled.
Run the circuit with no gates at all — a four-qubit register in $|0000\rangle$, where $\langle ZZZZ\rangle$ is exactly $+1$:
GATE-FREE circuit: <ZZZZ> = +0.96228 error 0.03772
There is a floor of 0.038 that has nothing to do with gates. It is readout error, and folding does not amplify it: doubling the gate count leaves the number of measurements unchanged, so the readout contribution is identical at every scale factor. Extrapolating in gate count cannot remove a term that is constant in gate count.
$$\text{error}(\lambda) = \underbrace{\lambda \cdot \varepsilon_{\text{gate}}}_{\text{ZNE removes this}} \;+\; \underbrace{\varepsilon_{\text{readout}}}_{\text{invariant under folding}}$$
ZNE can only remove the noise it can scale. Of the total 0.08228 error, roughly 0.045 was gate error and 0.038 was readout — and ZNE went after the smaller half while leaving the larger untouched.
This is not a flaw in ZNE. It is a specification of what ZNE is for, and it immediately implies the fix.
The gate-free probe understates its own floor
There is an arithmetic discrepancy in the numbers above, and it is worth chasing rather than rounding away.
If the readout floor is 0.03772 and errors are additive, then removing readout error should leave $0.08228 - 0.03772 = 0.04456$. §13.7 measures what readout mitigation actually leaves: 0.02954. It removed 0.05274 — 1.40× the floor it was supposed to be capped by.
The technique cannot remove more than exists, so the floor is measured wrong. And the reason is structural, not statistical: a gate-free circuit measures readout error on $|0\dots0\rangle$ and nothing else. That is the best column of the assignment matrix. A GHZ state puts half its population in $|1\dots1\rangle$, which is the worst.
The published matrix from §13.2 says so directly:
prepared assignment fidelity parity <ZZ>
00 0.9897 +0.97950
01 0.9854 -0.97090
10 0.9851 -0.97040
11 0.9800 +0.96020
Every excited qubit costs about 0.0047 of assignment fidelity, and the two-excited case costs 0.0097 — slightly more than twice one. Chapter 39's timings supply the mechanism: a measurement takes 1,560 ns, against $T_1$ values ranging 15.2–483.0 μs, so a qubit in $|1\rangle$ has a real chance of relaxing to $|0\rangle$ during the readout. A qubit in $|0\rangle$ has nowhere to fall.
Carry that through to a Bell state, which is the two-qubit version of the same asymmetry:
readout bias a gate-free probe reports (|00> only) 0.02050
readout bias a Bell state actually suffers 0.03015
understatement 1.471x
1.47× predicted from the two-qubit matrix; 1.40× observed in the four-qubit budget. Two independent routes to the same discrepancy, and the discrepancy has a name.
⚛️ The Physics Underneath — readout error is state-dependent, and every model in this chapter pretends it is not.
The assignment matrix is honest about this — the asymmetry is right there in its columns, which is why inverting the full matrix works. The convenient summaries are not:
- A gate-free probe reports the $|0\dots0\rangle$ column and calls it "the readout floor."
- A tensored or M3 calibration learns a per-qubit $2\times2$ response, which does capture the $0\!\to\!1$ / $1\!\to\!0$ asymmetry — but assumes it is the same regardless of what the other qubits are doing.
- §13.5's decomposition $\varepsilon_{\text{gate}} + \varepsilon_{\text{readout}}$ treats the readout term as one number.
All three are useful. None is exact, and the direction of the error is always the same: the floor you measure on $|0\dots0\rangle$ is a lower bound on the floor your circuit experiences.
Which is good news for §13.7 and bad news for anyone reporting an error budget. Readout mitigation outperformed the additive model's prediction by 40% — a pleasant surprise that is really a mis-specified model, and a reminder that a decomposition which sums to the right total can still have the wrong terms in it.
13.6 Dynamical Decoupling
While a qubit sits idle, it dephases. Dynamical decoupling fills idle time with pulse sequences
that average the unwanted evolution away — the quantum analogue of a spin echo. An X X pair, or the
four-pulse XY4, applies the identity while flipping the qubit through configurations whose phase
accumulations cancel.
from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDynamicalDecoupling
durations = backend.target.durations()
dd = PassManager([
ALAPScheduleAnalysis(durations),
PadDynamicalDecoupling(durations, dd_sequence=[XGate(), XGate()]),
])
isa_dd = dd.run(isa_circuit)
Now the honest part, measured on a circuit with a deliberately long idle period — two qubits hold a Bell state while two others churn through 30 layers of gates:
variant X added delays correct delta
(no DD) 0 0 0.9801 --
ALAP, skip_reset=True (default) 4 132 0.9801 +0.0000
ALAP, skip_reset=False 254 382 0.9178 -0.0623
ASAP, skip_reset=False 254 382 0.8796 -0.1005
Two separate problems, both worth knowing.
The default configuration did nothing
Four X gates inserted across a 213-deep circuit, and the result unchanged to four decimal places.
PadDynamicalDecoupling defaults to skip_reset_qubits=True, which skips DD on qubits that have not
yet been acted on. Combined with ALAP scheduling — which pushes operations as late as possible — the
Bell pair's gates land at the end, and the long idle stretch sits at the beginning, on qubits still
in their reset state. DD skips all of it.
🐛 Debug This — You can enable dynamical decoupling and have it do nothing.
The pass ran. It reported no error. It inserted 132 delay instructions and four
Xgates. The circuit got two layers deeper. And the result did not change at all, because the DD was placed where there was no idle time to protect.Verify, do not assume:
python added = isa_dd.count_ops().get("x", 0) - isa.count_ops().get("x", 0) print(f"DD inserted {added} X gates") # 4 across a 213-deep circuit = suspiciousA handful of pulses in a long circuit means DD is not engaging. Expect hundreds. Then check whether the numbers moved — and if the pass is doing nothing, changing the sequence type will not help, because the problem is placement, not sequence.
This is Chapter 11's testing trap again in a new costume: a technique that never reaches the case it is meant to handle proves nothing, and reports success.
When it did engage, it made things worse
254 X gates, and correctness fell from 0.9801 to 0.9178.
That result is correct and it is an artifact of the simulator, which makes it worth stating carefully.
🔬 Honest Assessment — You cannot evaluate DD on a fake backend, in principle.
DD works against correlated, slowly-varying noise — low-frequency dephasing that a pulse echo can reverse because it is still there, unchanged, when the echo arrives.
Chapter 11 §11.6 recorded that
NoiseModel.from_backendassumes independent, memoryless (Markovian) errors, with no correlations and no drift. In a memoryless model there is nothing for an echo to reverse — each moment's noise is freshly random and uncorrelated with the last.So in this simulation DD can only contribute the error of its own 254 pulses, and that is exactly what the numbers show. The simulator lacks the physics DD exists to address.
The lesson is not "DD is useless" — on real hardware it is often a genuine and cheap win. The lesson is that the fake-backend path, which has been right about everything else in this book, is structurally incapable of telling you anything about this one technique. Knowing which questions your instrument cannot answer is the same skill as §12.7 step 2, applied to a simulator instead of a circuit.
Evaluate DD on hardware, against a control, or not at all.
★ Chapter 31 ran the experiment properly, and got the same answer
The result above is one run of one circuit with no error bar. That is exactly the shape of claim this book distrusts, and Chapter 31 §31.4 does the version that survives scrutiny: Chapter 29's ansatz, six seeds, 20,000 shots each, scored as $1-\text{TVD}$ against the noiseless distribution.
configuration x added delays 1-TVD std
no DD 0 0 0.9288 0.0017
XX dynamical decoupling 20 159 0.9235 0.0025
XY4 dynamical decoupling 36 175 0.9231 0.0023
XX vs no DD: -0.0053 +/- 0.0012 SIGNIFICANT
XY4 vs no DD: -0.0057 +/- 0.0012 SIGNIFICANT
Significantly worse, at 4.4 standard errors. Different circuit, different sequence, different metric, six samples instead of one — and the same verdict as this section's $-0.0623$.
Two things Chapter 31 establishes that §13.6 cannot, and both matter for reading either result:
The comparison is paired. All three arms are built from one transpiled circuit, so layout and routing are common-mode and cancel from the difference. That is why $\pm 0.0012$ is narrow enough to resolve a 0.005 effect. It is the correct error bar for "does DD help on this circuit?" and the wrong one for "does DD help on circuits like this one" — Chapter 39 §39.6 measured the latter separately and found 24 transpiler seeds spanning fidelity 0.5755 to 0.7911, a 2.03× spread in error, two orders of magnitude larger than the effect.
The break-even is computable. DD's measured cost in that simulation is 0.0053 against a total dephasing exposure of 0.0409, so DD needs to refocus only 13.0% of the dephasing to pay for itself. That is a low bar, which is precisely why the Markovian verdict says nothing about hardware: the model sets the refocusable fraction to zero by construction.
The transferable lesson is about which techniques are actually free. §13.9's table lists both DD and twirling as costing "no extra circuits," and that is true — and misleading. DD inserts real pulses that carry real gate error; twirling is compiled away and inserts nothing. A technique that is free in circuits is not necessarily free in error. It is why twirling is "always" and DD is "hardware, with a control."
⚙️ Under the Transpiler — the runtime's DD defaults are the opposite of the pass's.
§13.6's four-
Xno-op came fromPadDynamicalDecoupling, whose signature defaultsskip_reset_qubits=True. The runtime'sDynamicalDecouplingOptionsdefaults the same field toFalse:
text PadDynamicalDecoupling(...) skip_reset_qubits = True DynamicalDecouplingOptions skip_reset_qubits = False enable = False sequence_type = "XX" scheduling_method = "alap"The same conceptual knob, spelled two ways, ships with opposite defaults. The hand-built pass silently declines to protect qubits fresh out of reset; the runtime silently protects them.
Which means §13.6's headline failure is a property of the hand-built path specifically. The runtime path would have engaged — into the $-0.0623$ result, on this simulator. Neither default is wrong;
alapmatches on both sides, and skipping reset qubits is defensible reasoning about qubits with no coherence to lose.The lesson is that "DD is on" names two different behaviours. Count the inserted pulses whichever path you took.
One practical note: XY4 requires a Y gate, which is not in IBM's native basis:
TranspilerError: 'Duration of y on qubits [0] is not found.'
Use sequences built from X and Rz, or the runtime's named sequence_type values ("XX",
"XpXm", "XY4"), which handle basis translation for you.
13.7 Ordering: Making Techniques Compound
The chapter's most useful result, and it follows directly from §13.5's diagnosis.
If ZNE cannot remove readout error, then remove the readout error first and let ZNE work on what remains — which is now entirely gate error, exactly the thing folding scales.
Measured, same four-qubit GHZ, 16 calibration circuits for the $16\times16$ assignment matrix (condition number 1.061):
scale <ZZZZ> raw <ZZZZ> readout-mitigated
1 +0.91772 +0.97046
3 +0.89392 +0.94527
5 +0.87012 +0.92014
7 +0.84668 +0.89533
unmitigated + ZNE (linear)
raw +0.91772 (0.08228) +0.92950 (0.07050) -14%
readout-mitigated +0.97046 (0.02954) +0.98291 (0.01709) -42%
ZNE removes 14% of the raw error and 42% of the readout-mitigated error. Same technique, same scale factors, same shots — three times more effective because it ran second.
The mechanism is exactly §13.5's equation. Applied to the raw signal, extrapolation targets $\lambda\varepsilon_{\text{gate}}$ while $\varepsilon_{\text{readout}}$ sits underneath it as an irreducible floor limiting the achievable improvement. Remove that floor first and extrapolation has a clean, fully-scalable signal to work on.
Combined: 0.08228 → 0.01709, an 79% error reduction.
🧱 Project Checkpoint —
vqelab/mitigation.py: the mitigation layer.Chapter 12's
backends.pychooses good qubits. This adds the layer that cleans up what is left.
AssignmentMatrix— builds from a backend and layout, caches the calibration (it is valid for hours, not for one job), exposes.condition_numberand.rank, and refuses to apply itself when rank-deficient, raising with the offending qubit rather than returning nonsense.
fold(circuit, factor)— logical-level global folding, with an assertion that the factor is odd and a guard against being handed an ISA circuit.
zne(circuits, values, extrapolator)— linear, quadratic, and exponential fits, returning all three plus their spread, because agreement among extrapolators is the diagnostic for whether extrapolation is justified.
mitigate(backend, circuit, observable, layout)— the whole stack in the right order: preflight (Ch. 12) → readout mitigation → ZNE → aMitigationRecordcarrying every intermediate value and the total shot cost.Its tests assert the ordering result: ZNE must improve a readout-mitigated value by more than it improves a raw one. That is a property of the physics, and it is the one thing that would break if someone reordered the pipeline.
13.8 Probabilistic Error Cancellation and Twirling
Two more techniques, in brief, because their cost profile puts them in a different category.
Probabilistic error cancellation (PEC) is the most principled technique available. Learn the noise channel precisely, express the inverse channel as a signed combination of implementable operations, then sample from that combination and reweight. It is unbiased: with enough shots, PEC converges to the exact noiseless value.
The catch is in "enough shots." The sampling overhead grows exponentially in circuit depth, with a base set by how noisy the gates are:
$$\text{shots required} \;\propto\; \gamma^{2d}, \qquad \gamma > 1$$
PEC is therefore exact and unaffordable — excellent for small circuits where you need a gold-standard number, unusable beyond modest depth. It is the clearest illustration of §13.1's point that mitigation trades shots for accuracy at a worsening rate.
The overhead is not a metaphor, and the runtime caps it
$\gamma^{2d}$ is easy to nod at. Put numbers in it and the shape becomes concrete.
The runtime's PecOptions carries a max_overhead field whose documented default is 100, and a
noise_gain defaulting to "auto" — a value in $[0,1]$ chosen per circuit "based on the learned noise
strength, max_overhead, and the depth of the PUB," where 0 removes the full learned noise and 1
removes none.
Read those two together and a consequence falls out that is easy to miss:
🔬 Honest Assessment — the default PEC configuration does not fully cancel the noise.
"PEC is unbiased" is a statement about the algorithm at full inversion. The default configuration caps sampling overhead at 100× and, when full inversion would cost more than that, scales the inversion back and returns a partially-cancelled estimate.
A partially-cancelled estimate is a biased estimate. So the real choice is: unbiased at whatever overhead the circuit demands, or biased under a cap you chose. There is no configuration that is both exact and bounded, and the default picks bounded.
This is not a criticism of the default — an uncapped PEC job could consume an arbitrary amount of QPU time before returning anything. It is a warning that
pec_mitigation = Truedoes not mean "exact," and that a PEC result needs itsmax_overheadand realizednoise_gainreported alongside it for the same reason §13.4 says a mitigated value needs its resilience level.
Where the cap binds, from $\gamma^{2d} = 100$:
per-layer gamma depth at which the 100x cap binds
1.01 231
1.02 116
1.05 47
1.10 24
And the growth on the other side of it:
gamma d shot multiplier gamma^2d
1.02 10 1.49
1.02 50 7.24
1.02 100 52.5
1.02 200 2,755
1.05 10 2.65
1.05 50 132
1.05 100 17,290
1.05 200 2.99e+08
This book has not measured $\gamma$ on a device, so read those as the consequences of the formula at plausible per-layer overheads rather than as a device measurement. The structure is what matters: at $\gamma = 1.05$, a circuit of depth 50 costs 132× the shots and one of depth 200 costs $3\times10^8$. Doubling the depth squares the cost. No improvement in gate fidelity changes that; it only changes which depth the wall sits at.
Compare against the constant-factor techniques. Readout mitigation cost 6.4% more variance (§13.2) and $2^n$ calibration circuits. The chapter's whole stack cost 12.00× (§13.1). PEC is in a different category not because it is worse but because its cost has depth in the exponent — the same distinction §13.1 drew between mitigation and correction, drawn again one level down inside mitigation itself.
One API note: the runtime refuses pec_mitigation and zne_mitigation together, raising
ValueError. They are competing answers to the same question, not layers of a stack.
Pauli twirling does not reduce error; it reshapes it. Sandwiching each gate in randomly chosen Pauli operators (compiled away classically) converts arbitrary coherent noise into stochastic Pauli noise of the same average strength.
That sounds like it accomplishes nothing, and it enables everything else:
- Coherent errors accumulate linearly and can conspire; stochastic errors accumulate as a random walk. Twirling converts a worst case into an average case.
- ZNE and PEC both assume a noise model. Twirling makes the actual noise match the assumed model,
which is why
twirling.enable_gates = Trueaccompanies the higher resilience levels.
Twirling is cheap and it makes the expensive techniques work as advertised. Leave it on.
📐 Math Aside — Why twirling turns a quadratic into a linear, and why that is worth a factor of $d$.
"Coherent errors conspire, stochastic errors random-walk" is the standard summary. The exponents are where the argument actually lives.
Suppose each gate applies a small unwanted rotation $R_z(\delta)$ — a miscalibration, identical every time. Over $d$ gates the angles add, because they are the same rotation about the same axis:
$$\theta_{\text{coherent}} \;=\; d\,\delta > \qquad\Longrightarrow\qquad > \text{infidelity} \;\sim\; \theta^2 \;=\; d^2\delta^2$$
Quadratic in depth. Now twirl. Conjugating the gate by a uniformly random Pauli maps $R_z(\delta) \to R_z(\pm\delta)$ with equal probability, and the compiler absorbs the Paulis, so nothing is added to the circuit. The accumulated angle becomes a sum of $d$ independent $\pm\delta$ steps — a random walk:
$$\mathbb{E}[\theta] = 0, > \qquad \mathbb{E}[\theta^2] \;=\; d\,\delta^2 > \qquad\Longrightarrow\qquad \text{infidelity} \;\sim\; d\,\delta^2$$
Linear in depth. The ratio between the two is $d$. At depth 100 that is a factor of one hundred in the error contributed by a systematic over-rotation, for zero extra circuits, zero extra shots, and zero extra gates.
Twirling does not make $\delta$ smaller. It changes the exponent on $d$.
And this is the second reason it belongs under ZNE. §13.5's derivation assumed a single depolarizing parameter per gate, giving $\langle O\rangle(\lambda) = (1-p)^{\lambda G}$. A coherent error does not have that form — its infidelity grows as $\lambda^2$, not $\lambda$ — so folding it and fitting a line extrapolates the wrong curve to the wrong intercept. Twirling makes ZNE's assumption true rather than assumed, which is why the runtime turns
twirling.enable_gateson at exactly the resilience level that turns ZNE on (§13.4).The book's recurring line is that every remedy is denominated in the currency of the disease. Twirling is the exception, and it is instructive: it costs nothing because it removes nothing. It only changes which distribution the error is drawn from.
13.9 What Each Technique Costs
The table to consult before enabling anything.
| Technique | Extra circuits | Removes | Measured benefit | Verdict |
|---|---|---|---|---|
| Readout mitigation | $2^n$, or ~2 tensored, or M3 | measurement error | −64% error | always |
| Twirling | none (randomizations) | coherent → stochastic | enables the others | always |
| Dynamical decoupling | none | idle dephasing | untestable locally | hardware, with a control |
| ZNE | 3–5× circuits, 3–12× gates | scalable gate error | −14% raw, −42% after readout | when accuracy matters |
| PEC | $\gamma^{2d}$ — exponential | everything, exactly | exact | small circuits only |
Read the middle column. Each technique removes one thing. Applying a technique to a circuit whose dominant error it does not target costs shots and returns nothing — or, as §13.3 measured, makes the result worse.
The decision procedure:
- Diagnose first (Chapter 11 §11.7, Chapter 12 §12.7). Know which channel dominates.
- Fix the layout first (Chapter 12 §12.3). Free, and worth more than any technique here — a 3.4× correctness swing against readout mitigation's 4×, at zero shot cost.
- Readout mitigation always. Best return, lowest cost, and it makes ZNE work better.
- Twirling always. Free, and the other techniques assume it.
- ZNE when you need the number to be right, budgeting 3–5× the shots.
- PEC only on small circuits where you need a reference value.
- Report what you used. A mitigated expectation value without its mitigation stack is not a reproducible result.
💰 Cost and Queue — what a 12× shot multiplier does to a real workload.
This chapter's stack cost 196,608 shots against an unmitigated 16,384 — 12.00× (§13.1). That multiplier is not a one-time charge. It applies to every circuit, every parameter update, every iteration.
text workload unmitigated x12 Ch.33 inference, 1M predictions 27.8 QPU hours 334 QPU hours Ch.37 QAOA p=3 to eps=0.01 22 min 4.4 hours Ch.36 crossover at 50 orbitals 6.06e8 QPU-yr 7.3e9 QPU-yrRead the last row. Multiplying $6.06\times10^{8}$ QPU-years by twelve produces $7.3\times10^{9}$ QPU-years, and nothing about the situation has changed. When the base number is an exponential, the mitigation multiplier is not the problem and removing it is not the solution.
That is §13.1's constant-factor argument in its most concrete form: mitigation moves a constant, and the wall is an exponent.
Two practical notes. Chapter 39 measured a 120-iteration VQE loop taking 10 hours as separate jobs against 5 minutes inside a session, and batching ~100 circuits at once running ~99× faster — mitigation's extra circuits are exactly the kind that batch well, so the wall-clock cost of the 12× is far below 12× if you submit them together. And Chapter 39's three billing models for one identical VQE run — $50 per-minute, $7,432 per-shot, $185,542 trapped-ion — mean the multiplier lands on whichever of those you are actually paying, and a per-shot contract is where a mitigation stack gets expensive fastest.
13.10 Summary
Mitigation is not correction. It recovers accurate expectation values from noisy runs by spending extra shots and classical post-processing. It never repairs a single run, so it does nothing for Shor or Grover; it is essential for VQE and QAOA. Its overhead grows with depth, so it buys a constant factor of room in front of the exponential wall rather than moving the wall.
Readout mitigation is the highest-return technique. Build the assignment matrix $A$ from $2^n$
calibration circuits, then solve $A\mathbf{p}_{\text{true}} = \mathbf{p}_{\text{obs}}$. On a Bell
state it cut L1 error from 0.0398 to 0.0099 (4×); on a four-qubit $\langle ZZZZ\rangle$ it removed
64% of the error. Use solve, not inv, and check the condition number.
It fails in three distinct ways. On a pair with a dead entangling gate it made the result worse (1.0305 → 1.0654), because it corrects readout error and the problem was gate error. On the stuck qubit the assignment matrix is singular — rank 2 of 4 — and mitigation is impossible, not merely ineffective: you cannot invert information that was destroyed. And it costs $2^n$ calibration circuits, which is a million at 20 qubits — use tensored mitigation or M3.
ZNE amplifies noise deliberately and extrapolates to zero. Fold the logical circuit ($U \to U(U^\dagger U)^k$), never the ISA one. Measured decay was clean and near-linear, and linear, quadratic, and exponential extrapolators agreed within 0.001 — agreement among extrapolators is the diagnostic that extrapolation is justified.
ZNE only removes noise it can scale. A gate-free circuit still gave $\langle ZZZZ\rangle = 0.96228$ — a readout floor of 0.038 that folding leaves untouched, since folding changes gate count and not measurement count. Alone, ZNE removed just 14% of the error for 12× the gate cost.
Order matters, and this is the chapter's most useful result. Removing the readout floor first leaves a purely scalable signal, and ZNE becomes three times more effective: −14% on raw, −42% after readout mitigation. Combined: 0.08228 → 0.01709, a 79% reduction.
Dynamical decoupling requires verification. The default configuration inserted four X gates into
a 213-deep circuit and changed the result by exactly zero — ALAP scheduling plus
skip_reset_qubits=True placed DD where no idle time existed. Count the inserted pulses; expect
hundreds, and be suspicious of a handful. And DD cannot be evaluated on a fake backend at all:
from_backend noise is Markovian, DD works against correlated noise, so there is nothing for the echo
to reverse and the pulses only add their own error (−0.0623 when forced to engage).
Twirling is free and makes everything else work, converting coherent noise into the stochastic noise that ZNE and PEC assume. PEC is exact and exponentially expensive — small circuits only.
resilience_level has no effect in local testing mode. Levels 0, 1, and 2 return bit-identical
results against a fake backend, with a warning most people never read.
Fix the layout before you mitigate anything. Chapter 12's 3.4× correctness swing cost zero extra shots and exceeds what any technique in this chapter delivers.
Next: Chapter 14 — Part III begins, and we leave Qiskit. Google's Cirq makes different choices at almost every level: explicit moments instead of inferred scheduling, a different qubit model, and a philosophy that treats timing as the programmer's business. Having spent seven chapters learning one framework deeply, the fastest way to understand what was Qiskit and what was quantum computing is to write the same circuits in something else.