Chapter 13 — Key Takeaways (Error Mitigation)

The mitigation page. §13.7's ordering result is the thing to remember.

★ Mitigation is not correction

Mitigation Correction
Fixes the expectation value the quantum state
Qubit overhead none 100–1000×
Shot overhead large, grows with depth modest
Deep circuits? no yes
Available today yes not at scale
Correct single shot? no yes

Useless for Shor/Grover (they need one correct bitstring). Essential for VQE/QAOA.

Mitigation buys a constant factor of room in front of the exponential wall. It does not move the wall. Circuits mitigation rescues are usually classically simulable — do not call it a path to advantage.

★★ The headline: ORDER MATTERS

4-qubit GHZ, $\langle ZZZZ\rangle$, true value $+1$:

                       unmitigated          + ZNE (linear)
  raw                  +0.91772  (0.08228)  +0.92950  (0.07050)   -14.3%
  readout-mitigated    +0.97046  (0.02954)  +0.98291  (0.01709)   -42.1%
  • Readout mitigation alone: −64% for 16 extra circuits.
  • ZNE alone: −14% for 12× the two-qubit gate count.
  • Both, in the right order: −79%.

ZNE is 3× more effective when it runs second — same technique, same scales, same shots.

$$\text{error}(\lambda) = \underbrace{\lambda\,\varepsilon_{\text{gate}}}_{\text{ZNE removes}} + \underbrace{\varepsilon_{\text{readout}}}_{\text{invariant under folding}}$$

RULE: remove the noise a technique CANNOT scale, before applying a technique that works by scaling noise.

Readout mitigation — the assignment matrix

$$\mathbf{p}_{\text{obs}} = A\,\mathbf{p}_{\text{true}} \Longrightarrow \mathbf{p}_{\text{true}} = A^{-1}\mathbf{p}_{\text{obs}}$$

Measurement error is classical — it acts after the state is gone — so it is an ordinary stochastic matrix. Build from $2^n$ gate-free calibration circuits.

  qubits [122,123]      00        01        10        11    <- prepared
                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

Diagonal ~0.985 · single flips ~0.01 · double flips ~0.0001 (100× rarer) → diagonally dominant → safe inversion.

  Bell state L1 error:  0.0398 -> 0.0099      4.0x better

Use np.linalg.solve(A, raw), not inv(A) @ raw. Always check the condition number.

★ Three ways it fails

  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

1. It can make things worse. Pair (9,8) has an ECR error of 1.0 — the state died at the gate, not the measurement. Readout mitigation only removes readout error; applied elsewhere it amplifies statistical noise and buys nothing.

2. It can be impossible. The stuck qubit's matrix:

    00   0.0000  0.0000  0.0000  0.0000     <- rows 00,10 never occur
    01   0.9685  0.9666  0.0159  0.0131     <- cols 00,01 IDENTICAL
    10   0.0000  0.0000  0.0000  0.0000
    11   0.0315  0.0334  0.9841  0.9869
  det 0.0    rank 2 (should be 4)

Rank deficit = qubits whose information was destroyed. $A^{-1}$ does not exist. You cannot invert information that was destroyed. Compute the rank; if deficient, no shot count helps.

3. It does not scale. $2^n$ calibration circuits: n=10 → 1,024; n=20 → 1,048,576; n=50 → 10¹⁵. The exponential is in the calibration. Use tensored (2 circuits, assumes independence) or M3 (never forms the matrix).

ZNE — amplify deliberately, extrapolate to zero

$$U \longrightarrow U(U^\dagger U)^k \quad\text{(gate count} \times (2k{+}1))$$

  scale   ecr   depth    <ZZZZ>
     1      3     13    +0.91772
     3      7     25    +0.89392
     5     11     37    +0.87012
     7     15     49    +0.84668

  linear      +0.92950     quadratic  +0.92975     exponential  +0.93039
  spread across extrapolators: 0.00090

⚠️ Fold the LOGICAL circuit, then transpile. isa.inverse() emits sxdg: IBMInputValueError: The instruction sxdg on qubits (125,) is not supported.

Agreement among extrapolators is THE diagnostic. Divergence = extrapolating past the data. Here they agreed to 0.0009 — the extrapolation was fine; the question was wrong.

★ Why it stalled at 0.930

  GATE-FREE circuit:  <ZZZZ> = +0.96228   ->  readout floor 0.03772
  total error 0.08228 = gate 0.04456 + readout 0.03772

Folding multiplies gates, not measurements, so readout error is identical at every scale. ZNE can only remove the noise it can scale — and it went after the smaller half.

Dynamical decoupling — verify it is doing anything

  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

The default did NOTHINGskip_reset_qubits=True + ALAP (schedules late) put the idle period at the start, on still-in-reset qubits. DD skipped all of it.

added = isa_dd.count_ops().get("x", 0) - isa.count_ops().get("x", 0)   # expect HUNDREDS

🔬 You CANNOT evaluate DD on a fake backend — in principle. DD works against correlated, slowly-varying noise. from_backend is Markovian (Ch. 11 §11.6): memoryless, uncorrelated. Nothing for an echo to reverse, so the 254 pulses contribute only their own error. The simulator lacks the physics. Evaluate on hardware, against a control, or not at all.

⚠️ XY4 needs a Y gate — not in IBM's basis (TranspilerError: Duration of y ... not found).

PEC and twirling

PEC — learn the noise channel, express its inverse as a signed combination, sample and reweight. Unbiased and exact, and the overhead is $\propto \gamma^{2d}$, exponential in depth. Small circuits only.

Twirling — does not reduce error, reshapes it: coherent → stochastic Pauli noise. Coherent errors accumulate linearly and conspire; stochastic ones as a random walk. And ZNE/PEC both assume a noise model — twirling makes the real noise match it. Free. Leave it on.

★ The cost table

Technique Extra circuits Removes Measured Verdict
Readout mitigation $2^n$ / ~2 / M3 measurement error −64% always
Twirling none coherent → stochastic enables others always
DD none idle dephasing untestable locally hardware + control
ZNE 3–5× circuits, 3–12× gates scalable gate error −14% raw, −42% after when accuracy matters
PEC $\gamma^{2d}$ everything, exactly exact small circuits only

Each technique removes ONE thing. Applied to the wrong channel it costs shots and returns nothing — or makes things worse.

Procedure: diagnose (Ch. 11 §11.7 / Ch. 12 §12.7) → fix the layout first (Ch. 12 §12.3, free, 3.4× swing) → readout mitigation always → twirling always → ZNE when accuracy matters → PEC only small → report the stack.

🗝️ resilience_level does nothing locally

  level 0: <ZZ> = +0.96387   level 1: +0.96387   level 2: +0.96387
  UserWarning: The resilience_level option has no effect in local testing mode.

Resilience options are server-side. The one part of the workflow fake backends cannot rehearse — which is why this chapter builds everything by hand.

Common pitfalls

  • Calling mitigation "correction," or a path to advantage.
  • Mitigating before fixing the layout.
  • Applying a technique to a channel it does not target.
  • inv instead of solve; not checking the condition number or rank.
  • Folding an ISA circuit.
  • Reading extrapolator agreement on a wrong answer as failure of the fit.
  • Assuming DD ran because it was enabled.
  • Reporting a mitigated value without its stack.

Project piece added this chapter

vqelab/mitigation.pyAssignmentMatrix (caches; exposes .condition_number, .rank, .dead_qubits(); refuses when singular), fold() (guards odd factors and ISA input), extrapolate() (three methods + spread), MitigationRecord. 10 tests pass, including test_zne_is_more_effective_after_readout_mitigation — the ordering result as an executable assertion.