Education / Quantum Programming

Quantum Programming · Chapter 13

Noise and Reliability

How can we distinguish a noisy experiment from an incorrect program?

In this chapter · 3 sections
  1. 13.1 Noise Models
  2. 13.2 Reliability Techniques
  3. 13.3 Interpreting Discrepancies
Reading guide · preparation and approach

Starting point: Earlier programming chapters; relevant constructions from Quantum Computing and Quantum Algorithms.

Anchor example: Predict the effect of a depolarizing gate error, compare it with a noisy simulation of the same circuit, and identify what a hardware comparison would add.

This lesson includes explanations, worked examples, and problems with solutions. Advanced results whose proofs are outside the chapter are identified explicitly.

Every check so far assumed an ideal machine. Then a failed check meant a bug, and a passing one meant the logic was right, within the sampling tolerance we derived. A real device is noisy. Its gates are slightly wrong, its qubits drift while they wait, and its readout sometimes reports the wrong bit.

When a result disagrees with our prediction, how can we tell a noisy experiment from an incorrect program? We start by modelling noise precisely enough to predict its effect, because a correct program on a noisy device must still have a predictable result. Some techniques reduce noise during a run and others correct for it afterwards, and each rests on assumptions we must state. With those pieces we can list the possible causes of a discrepancy and the order in which to rule them out.

One noisy X gate is enough to show every distinction.

13.1 Noise Models

An X gate should turn \(\ket0\) into \(\ket1\), yet a noisy device sometimes reports 0. Where did the error happen: in the gate, in the readout, or only in our finite sample? Before trying to correct anything, we need a model that separates these causes and predicts their effect. We define the two kinds of error first, then build a simulation that applies one of them.

13.1.1 Gate and readout errors

What can go wrong between the X gate and the reported bit? Either the gate leaves the qubit in a different state, or the state is right and the readout reports the wrong label. The first is a gate error, which changes the quantum state before measurement. The second is a readout error, which changes only the classical record.

A simple model of a gate error is depolarizing noise. With probability \(1-\lambda\) the gate acts correctly, and with probability \(\lambda\) the qubit is replaced by the completely random state \(I/2\). In density-matrix form, \(\mathcal E(\rho)=(1-\lambda)\rho+\lambda I/2\). The companion uses \(\lambda=0.1\) after X.

What probability of 1 should we then see? The intended state is \(\ketbra11\), so the noisy state is \(0.9\ketbra11+0.1\,I/2\). Its probability of 1 is \(0.9\cdot1+0.1\cdot\tfrac12=0.95\). So the parameter 0.1 is not the probability of a wrong bit. Half of the random replacements still give 1, and the wrong-bit probability is 0.05. A model’s parameter means only what its convention says.

13.1.2 Constructing noisy simulations

How do we tell a simulator to apply this error? Aer’s NoiseModel attaches errors to instructions by name. add_all_qubit_quantum_error(depolarizing_error(0.1, 1), ["x"]) says: after every x instruction on any qubit, apply one-qubit depolarizing noise of strength 0.1.

That attachment by name has a trap. Compilation can remove an instruction or replace it by others. If the compiled circuit no longer contains an x, an error attached to x never acts, and the simulation looks cleaner than intended. So we inspect the compiled circuit. The companion compiles with optimization_level=0, which makes no simplifications, and uses an X that the simulator supports directly, so the error stays attached where we put it.

A chosen noise model is a hypothesis about a device’s errors. It is not a calibrated description of any particular device.

Problem 13.1. For depolarization strength 0.2 after X, predict P(1).

Worked solution

The state is 0.8|1〉〈1|+0.2I/2, so P(1)=0.9.

13.2 Reliability Techniques

Knowing how noise acts, can we do anything about it? There are two broad answers. We can try to make fewer errors happen during the run, or we can accept the errors and correct the estimated quantity afterwards. Each works only under assumptions about the noise. We take reduction during the run first, then correction afterwards.

13.2.1 Error suppression

How can a program make fewer errors happen? The plainest ways reduce exposure. A better layout avoids swaps, and a shorter circuit gives noise less time and fewer gates to act on. Reducing errors during execution in such ways is called error suppression.

A less obvious method targets qubits that wait. Suppose an idle qubit slowly picks up an unwanted Z rotation \(R_z(a)\) during each half of its waiting time. If we apply X in the middle and again at the end, the total is \(X\,R_z(a)\,X\,R_z(a)\). Since \(XZX=-Z\), conjugating by X reverses the rotation, \(XR_z(a)X=R_z(-a)\), so the total is \(R_z(-a)R_z(a)=I\). Inserting such pulses into idle periods is called dynamical decoupling. It works only if the unwanted rotation stays the same over the waiting time.

That condition shows the general rule. A suppression technique adds operations and time, and if its assumption fails, the added gates can make the result worse. So we compare against a baseline run without the technique, and count the operations and duration it added.

13.2.2 Error mitigation and its assumptions

Suppose instead we accept the errors and correct the estimate afterwards. Estimating an ideal quantity from noisy data in this way is called error mitigation. It works by calibration, by inverting a model of the noise, or by extrapolating from runs at increased noise. How much can it recover, and at what price? Readout errors give a case we can compute exactly.

Suppose each reported bit is flipped with probability \(e\), the same for 0 and 1. A shot that should give \(s=\pm1\) then reports \(s\) with probability \(1-e\) and \(-s\) with probability \(e\). Its expected reported value is \((1-e)s-es=(1-2e)s\), so \[\langle Z\rangle_{\text{observed}}=(1-2e)\,\langle Z\rangle_{\text{true}}.\] If we know \(e\), dividing the observed mean by \(1-2e\) corrects it.

The price is variance. Dividing an estimate by \(1-2e\) multiplies its variance by \(1/(1-2e)^2\). As \(e\) approaches one half, the factor grows without bound, because the reported bits then carry almost no information. In practice \(e\) is itself estimated by calibration, so its uncertainty, and its drift between calibration and run, add to the error. The correction is right only if the model is right.

Problem 13.2. At readout flip rate e=0.1, how does inverse correction scale variance?

Worked solution

It multiplies variance by 1/0.8²=1.5625, even if e is known exactly.

13.3 Interpreting Discrepancies

We can now name four possible causes for a result that disagrees with its prediction: a wrong program, an approximate circuit, random shots, and noise that our model does not describe. How do we decide which one is responsible, and what may we claim once we have? We set out the order of diagnosis first, then what a report must include.

13.3.1 Noise, approximation, and sampling

Suppose the noisy X run gives \(P(1)=0.90\) instead of 0.95. Where do we look first? We work through the causes from the most controlled experiment to the least. An exact simulation tests the program’s logic, with no sampling and no noise. If the circuit is approximate by design, its known error bound tells us how far the exact result may legitimately move. A sampled ideal simulation adds only shot noise, whose size we can derive. Only then does a noisy simulation test the noise model, and a device test the device.

Each step changes one thing, so a discrepancy that first appears at some step points to what that step added. More shots shrink the random fluctuation, but they cannot repair a biased model. Comparing an exact simulator directly with noisy hardware skips every step, and its disagreement diagnoses nothing.

13.3.2 Bias, uncertainty, and reporting limits

What does the companion actually establish? It runs 8192 simulated shots and compares the frequency of 1 with the predicted 0.95. The standard error is \(\sqrt{0.95\cdot0.05/8192}\approx0.0024\), so the tolerance of 0.03 is about 12 standard errors and deliberately generous. With seed 31 the run gave 0.9485.

The check confirms that the simulator applies the model we wrote down, with the convention we derived. It does not show that depolarizing noise describes any device. Nor is its tolerance a confidence interval suitable for other experiments. So with the output we report the model, its parameter convention, the instructions it is attached to after compilation, the seed and the number of shots. A comparison with hardware would add a device, a time and a calibration, and it would need all the records of a hardware run.

"""Runnable companion for Quantum Programming, Chapter 13."""
from qiskit_aer import AerSimulator
from qiskit import QuantumCircuit, transpile
from qiskit_aer.noise import NoiseModel, depolarizing_error

qc = QuantumCircuit(1)
qc.x(0); qc.measure_all()
noise = NoiseModel()
noise.add_all_qubit_quantum_error(depolarizing_error(0.1, 1), ["x"])
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
counts = sim.run(transpile(qc, sim, optimization_level=0), shots=8192).result().get_counts()
p1 = counts.get("1", 0) / 8192
# E(rho) = .9 rho + .1 I/2, so expected P(1) = .95.
assert abs(p1 - 0.95) < 0.03
print("Simulated P(1):", p1, "analytic prediction: 0.95")

Companion script: chapter_13.py.

Problem 13.3. Can error mitigation certify a fault-tolerant computation?

Worked solution

No. Mitigation depends on a noise model and statistical processing. It does not supply an error-correcting logical computation or a general guarantee against arbitrary errors.

So far a person read each result and decided what to do next. Some programs let a classical optimizer make that decision, using estimates from a quantum circuit at every step. How do quantum measurements guide a classical optimization loop, and what does the noise in each estimate do to it?

Sources and further study: IBM, noise model construction.

Summary

Questions and worked problems

These exercises draw on the whole chapter. Attempt each question before opening a hint, explain your reasoning, then compare it with the worked solution.

1. Quick check §

With the depolarizing convention \(\mathcal E(\rho)=(1-p)\rho+pI/2\) attached to X, predict \(P(1)\) after X on \(\ket0\) for \(p=0.3\), and check it with a noisy Aer simulation.

Hint 1

\(P(1)=(1-p)\cdot1+p\cdot\frac12\).

Hint 2

The wrong-bit probability is \(p/2\), not \(p\).

Worked solution

\(P(1)=0.7+0.15=0.85\), so the bit is wrong with probability \(0.15=p/2\). The simulation with \(20\,000\) shots agrees to within sampling error. Reporting “\(30\%\) depolarizing” without its convention invites a factor-of-two misunderstanding of the error rate.

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error

noise = NoiseModel()
noise.add_all_qubit_quantum_error(depolarizing_error(0.3, 1), ["x"])
circuit = QuantumCircuit(1)
circuit.x(0)
circuit.measure_all()
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 20000
counts = sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts()
assert abs(counts.get("1", 0) / shots - 0.85) < 0.02

Revisit the relevant explanation

2. Quick check §

A readout error reports 1 with probability \(0.03\) for a true \(0\) and reports 0 with probability \(0.05\) for a true \(1\). Predict the reported \(P(1)\) for an idle qubit and for X\(\ket0\), and check with Aer’s ReadoutError.

Hint 1

The assignment matrix columns are the true states.

Hint 2

No gate error is included.

Worked solution

Idle qubit: reported \(P(1)=0.03\). After X: reported \(P(1)=0.95\). Readout errors change only the classical label, and their two directions can differ, so they must be calibrated with both preparations.

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, ReadoutError

noise = NoiseModel()
noise.add_all_qubit_readout_error(ReadoutError([[0.97, 0.03], [0.05, 0.95]]))
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 20000
def p1(flip):
    circuit = QuantumCircuit(1)
    if flip:
        circuit.x(0)
    circuit.measure_all()
    return sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts().get("1", 0) / shots
assert abs(p1(False) - 0.03) < 0.01 and abs(p1(True) - 0.95) < 0.01

Revisit the relevant explanation

3. Quick check §

Inverse correction of a symmetric readout error multiplies the variance of the estimated \(\langle Z\rangle\) by \(1/(1-2e)^2\). Evaluate this factor for \(e=0.05\) and \(e=0.3\). How many more shots are needed to keep the same precision?

Hint 1

\((1-2e)^2\) is \(0.81\) and \(0.16\).

Hint 2

Shots scale with the variance.

Worked solution

\(e=0.05\): factor \(1/0.81\approx1.23\), so about \(23\%\) more shots. \(e=0.3\): factor \(1/0.16=6.25\), more than six times the shots. Mitigation trades bias for variance, and the cost grows rapidly as the error approaches \(\frac12\).

Revisit the relevant explanation

4. Worked problem §

A noise model contains both a depolarizing error (\(p=0.1\)) on X and the asymmetric readout error above. (a) Predict \(P(1)\) for an idle qubit and after X. (b) Explain how running both experiments separates gate error from readout error. (c) Check the predictions with Aer.

Hint 1

After X, the true \(P(1)\) is \(0.95\) before readout.

Hint 2

Reported \(P(1)=0.03\,(1-q)+0.95\,q\) for true probability \(q\).

Worked solution

(a) Idle: only readout acts, so \(P(1)=0.03\). After X: the true \(P(1)\) is \(0.95\), and readout reports \(0.03\times0.05+0.95\times0.95\approx0.904\). (b) The idle experiment measures readout error alone (no X applied); the X experiment combines both. With readout calibrated from idle and from a separately trusted preparation, the extra loss in the X experiment is attributed to the gate. (c) The simulation gives about \(0.031\) and \(0.902\).

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, ReadoutError, depolarizing_error

noise = NoiseModel()
noise.add_all_qubit_quantum_error(depolarizing_error(0.1, 1), ["x"])
noise.add_all_qubit_readout_error(ReadoutError([[0.97, 0.03], [0.05, 0.95]]))
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 20000
def p1(flip):
    circuit = QuantumCircuit(1)
    if flip:
        circuit.x(0)
    circuit.measure_all()
    return sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts().get("1", 0) / shots
assert abs(p1(False) - 0.03) < 0.01
assert abs(p1(True) - (0.03 * 0.05 + 0.95 * 0.95)) < 0.01

Revisit the relevant explanation

5. Worked problem §

Mitigate the readout error for the state ry(\(\pi/3\))\(\ket0\), whose ideal \(P(1)\) is \(0.25\). (a) Calibrate the assignment matrix from idle and X runs. (b) Measure the noisy distribution of the test state. (c) Solve the linear system for the corrected distribution and compare with \(0.25\).

Hint 1

Assignment matrix \(A_{ij}=P(\text{report }i\mid\text{true }j)\).

Hint 2

Solve \(A\,p_{\rm true}=p_{\rm measured}\).

Worked solution

The calibration gives \(A\approx\begin{pmatrix}0.97&0.05\\0.03&0.95\end{pmatrix}\). The noisy test distribution reports \(P(1)\approx0.26\). Solving \(Ap=p_{\rm meas}\) returns \(P(1)\approx0.251\), close to the ideal \(0.25\). The correction assumes that readout errors do not depend on what happened before measurement and that the calibration still describes the device; it removes readout bias but does nothing about gate errors.

import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, ReadoutError

noise = NoiseModel()
noise.add_all_qubit_readout_error(ReadoutError([[0.97, 0.03], [0.05, 0.95]]))
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 50000
def p1(prepare):
    circuit = QuantumCircuit(1)
    prepare(circuit)
    circuit.measure_all()
    return sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts().get("1", 0) / shots
r0 = p1(lambda c: None)
r1 = p1(lambda c: c.x(0))
A = np.array([[1 - r0, 1 - r1], [r0, r1]])
measured = p1(lambda c: c.ry(np.pi / 3, 0))
corrected = np.linalg.solve(A, [1 - measured, measured])
assert abs(corrected[1] - 0.25) < 0.01

Revisit the relevant explanation

6. Worked problem §

A noise model attaches a strong depolarizing error to the instruction named h. A circuit applies H twice and measures. (a) Run it with the simulator’s default transpilation and with transpilation to the basis rz, sx, x. (b) Explain the different results. (c) What should be checked whenever a noise model is used?

Hint 1

Noise models attach errors by instruction name.

Hint 2

In the rz, sx, x basis, no instruction is named h.

Worked solution

(a) With h preserved, the two noisy Hadamards give \(P(1)\approx0.37\); after transpiling to rz, sx, x, the result is \(P(1)=0\): no noise at all. (b) The error is attached to the name h, and in the second circuit every H was rewritten as RZ and SX gates, so the model never fires. (c) Inspect the compiled circuit that is actually simulated and confirm that every intended noisy instruction appears with the name used in the model; otherwise a “noisy” simulation can silently be ideal.

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error

noise = NoiseModel()
noise.add_all_qubit_quantum_error(depolarizing_error(0.5, 1), ["h"])
circuit = QuantumCircuit(1)
circuit.h(0)
circuit.h(0)
circuit.measure_all()
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 20000
kept = sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts()
rewritten = sim.run(transpile(circuit, basis_gates=["rz", "sx", "x"], optimization_level=0), shots=shots).result().get_counts()
assert kept.get("1", 0) / shots > 0.3 and rewritten.get("1", 0) == 0

Revisit the relevant explanation

7. Worked problem §

Zero-noise extrapolation: each X carries depolarizing error \(p=0.05\), so \(\langle Z\rangle\) after one X is \(-(0.95)\). Amplify the noise by inserting pairs of X gates (logically the identity) to get noise scales \(\lambda=1,3,5\). (a) Predict \(\langle Z\rangle(\lambda)\). (b) Estimate the zero-noise value from \(\lambda=1,3\) with the Richardson formula \((3E_1-E_3)/2\). (c) Check with Aer and state the assumptions.

Hint 1

Each depolarizing step shrinks \(\langle Z\rangle\) by \(0.95\).

Hint 2

\(E(\lambda)=-(0.95)^\lambda\).

Worked solution

(a) \(E(\lambda)=-(0.95)^\lambda\): \(-0.950\), \(-0.857\), \(-0.774\). (b) \((3(-0.950)-(-0.857))/2\approx-0.996\), close to the ideal \(-1\). (c) Sampling with \(2\times10^5\) shots reproduces these values. The method assumes the inserted gates scale the noise by exactly \(\lambda\) and that \(E(\lambda)\) is close to linear over the range used; the extrapolated value also has larger statistical error than the raw measurements.

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error

noise = NoiseModel()
noise.add_all_qubit_quantum_error(depolarizing_error(0.05, 1), ["x"])
sim = AerSimulator(noise_model=noise, seed_simulator=31, max_parallel_threads=1)
shots = 200000
def z_mean(scale):
    circuit = QuantumCircuit(1)
    circuit.x(0)
    for _ in range((scale - 1) // 2):
        circuit.x(0)
        circuit.x(0)
    circuit.measure_all()
    p = sim.run(transpile(circuit, sim, optimization_level=0), shots=shots).result().get_counts().get("1", 0) / shots
    return 1 - 2 * p
e1, e3 = z_mean(1), z_mean(3)
assert abs(e1 + 0.95) < 0.01 and abs(e3 + 0.95 ** 3) < 0.01
assert abs((3 * e1 - e3) / 2 + 1) < 0.02

Revisit the relevant explanation

8. Misconception §

A student calibrates a symmetric readout flip rate of \(e=0.10\) and divides every measured \(\langle Z\rangle\) by \(1-2e\). By the time the experiment runs, the true rate has drifted to \(0.12\). (a) What fraction of the true value does the “mitigated” result report? (b) Why does collecting more shots not reveal the problem?

Hint 1

Measured mean \(=(1-2e_{\rm true})\langle Z\rangle\).

Hint 2

Corrected \(=\) measured\(/(1-2e_{\rm cal})\).

Worked solution

(a) The measured mean is \(0.76\langle Z\rangle\), and dividing by \(0.80\) gives \(0.95\langle Z\rangle\): a \(5\%\) bias remains after “mitigation.” (b) More shots shrink the statistical error around the biased value, making a wrong answer look more precise. Mitigation is only as good as its model and calibration; recalibrating close in time, interleaving calibration circuits, and reporting calibration uncertainty are part of the method.

Revisit the relevant explanation

9. Extension §

Full readout mitigation on \(n\) qubits calibrates a \(2^n\times2^n\) assignment matrix. (a) How many calibration circuits and matrix entries does this need for \(n=5\) and \(n=20\)? (b) If readout errors are independent per qubit, how does a tensor-product model reduce the cost? (c) What error does that assumption risk?

Hint 1

Full calibration prepares all \(2^n\) basis states.

Hint 2

Independent errors need one \(2\times2\) matrix per qubit.

Worked solution

(a) \(n=5\): \(32\) calibration circuits and \(1024\) entries. \(n=20\): about \(10^6\) circuits and \(10^{12}\) entries, infeasible. (b) With independent errors the matrix is a tensor product of \(n\) one-qubit matrices: two calibration circuits per qubit (or two in total, run in parallel) and \(4n\) numbers. (c) Correlated readout errors, such as crosstalk between neighbouring readout resonators, are ignored and leave residual bias. Scalable mitigation trades completeness for assumptions that must themselves be tested.

Revisit the relevant explanation

10. Extension §

An idle qubit suffers quasi-static dephasing that a pair of X gates (an echo) would refocus, reducing the idle error from \(\epsilon_{\rm idle}\) to nearly zero, but each X adds error \(5\times10^{-4}\). (a) For which \(\epsilon_{\rm idle}\) does adding the echo help? (b) What if the dephasing noise changes quickly during the idle window? (c) What should a program report when it adds suppression gates?

Hint 1

Two X gates add about \(10^{-3}\).

Hint 2

An echo cancels only noise that is constant over the window.

Worked solution

(a) The echo helps when \(\epsilon_{\rm idle}\) exceeds about \(10^{-3}\), the error added by its two gates. (b) If the noise varies during the window, the echo refocuses only part of it, and the break-even idle error is larger; for very fast noise the gates only add error. (c) A baseline without suppression, the added gate count and duration, and measured results for both, so that the benefit is demonstrated rather than assumed.

Revisit the relevant explanation

Search this book

Search the course.