Chapter 12 — Key Takeaways (Running on Real Hardware)

The hardware page. §12.7's decision procedure is the durable skill; everything else feeds it.

Choosing a backend — not by qubit count

  backend            qubits  2q gate   median 2q err   median RO    T1 (μs)   T2 (μs)
  fake_sherbrooke      127     ecr        0.00779       0.0198       278.4     170.0
  fake_torino          133     cz         0.00419       0.0229       185.0     140.9
  fake_kyiv            127     ecr        0.01172       0.0127       287.1     118.1
  fake_osaka           127     ecr        0.00693       0.0211       287.3     140.0

2.8× spread in gate error. Gate sets differ (ecr vs cz) — transpile against the real target.

No single best device. Best gate error has the worst readout, and vice versa.

Your circuit Optimize for
Shallow, few 2q gates readout error
Deep, many 2q gates two-qubit gate error
Long, idle qubits $T_2$

★★ The spread WITHIN a device dwarfs the spread between them

                       min      median        max      max/min
  readout error     0.0029      0.0198     0.5000       170.7x
  2q gate error     0.0035      0.0078     1.0000       288.2x
  T1 (μs)          73.2       278.4      514.9           7.0x
  T2 (μs)           2.6       170.0      488.8         185.4x

Between devices: 2.8×. Within one device: 288×.

Choosing good qubits matters ~100× more than choosing a good device.

  readout error > 10%:  [6, 9, 13, 16, 34, 52, 56, 57, 64, 70, 84, 92]   (12 of 127)
  ecr pairs error ≥ .99: [(6,5),(7,6),(8,9),(16,8),(56,52),(56,57),
                          (83,84),(85,84),(92,102)]                       (9 dead)

Chapter 4 found qubit 6 by accident. A five-line query finds all twelve in a second.

"A 127-qubit processor" is a statement about fabrication, not usable resource. Treat a device as a population you sample from, not a machine you run on.

readout_error is an AVERAGE, and the averaging hides things

$$\texttt{readout\_error} = \frac{P(1|0) + P(0|1)}{2}$$

qubit readout_error $P(1\mid 0)$ $P(0\mid 1)$ measured $P(1)$
6 0.2573 0.5044 0.0103 0.5044
70 0.1626 0.3188 0.0063 0.3262
84 0.5000 1.0000 0.0000 1.0000
92 0.3406 0.0127 0.6685 0.0142

Qubit 84 is not a coin flip — it is STUCK AT 1. Returns 1 from either input, all 4096 shots. $(1+0)/2 = 0.5$ is precisely what a stuck qubit averages to. Qubit 92 is the mirror image.

Measured behavior tracks $P(1|0)$, not the average. Threshold on both directions.

🗝️ Two paths disagree. NoiseModel.from_backend symmetrizes[[0.5,0.5],[0.5,0.5]]AerSimulator.from_backend gives {'0':2054,'1':2042}. SamplerV2(mode=backend) honors the asymmetry → {'1': 4096}, seed-independent. Same backend, same circuit, different physics.

"I simulated it with the device noise model" is ambiguous. Say which path.

★ Which direction dominates? It depends on the chip.

device $P(1\lvert 0) > P(0\lvert 1)$ reverse mean $P(1\lvert 0)$ mean $P(0\lvert 1)$
fake_sherbrooke 85 38 0.0585 0.0245
fake_torino 69 61 0.0512 0.0422
fake_kyiv 56 68 0.0281 0.0341
fake_osaka 51 76 0.0410 0.0428

Textbook expectation: $1 \to 0$ should dominate ($|1\rangle$ decays during readout). Sherbrooke contradicts it 85-to-38; Kyiv and Osaka agree with it; Torino is a coin toss.

The direction flips between devices — there is no rule to memorize, and that is the result. Chapter 2 drew this from 2 qubits; 513 qubits across 4 devices confirm it. Median asymmetry is 1.3–1.9×; Sherbrooke has 11 qubits over 10×.

Measure the asymmetry, don't inherit it.

★ Scoring layouts — and it works

def score_chain(path, target, two_q):        # LOWER IS BETTER
    total = sum(target["measure"][(q,)].error for q in path)
    for a, b in zip(path, path[1:]):
        p = target[two_q].get((a, b)) or target[two_q].get((b, a))
        total += 3 * (p.error if p and p.error is not None else 1.0)
    return total
  n=3: 394 connected paths   best (122,123,124) 0.0501   worst (83,84,85) 6.6016   131.9x
  n=5: 684 connected paths   best 0.1104   worst 10.0220   90.7x

Measured, 3-qubit GHZ:

  BEST    [122,123,124]   correct fraction 0.9727
  median  [ 67, 66, 65]                    0.9207
  WORST   [ 83, 84, 85]                    0.2844

97.3% vs 28.4% — a factor of 3.4, same circuit, same chip, same second.

The score is crude (errors combine multiplicatively, not additively; the ×3 is borrowed from SWAP cost). It does not need to be accurate — it needs to RANK, and it does.

Preflight check — refuse to submit onto readout > 10% (both directions) or gate error > 5%. Ten lines. Catches Chapter 4's dead qubit instantly.

Jobs

job = sampler.run([isa], shots=4096)
job.job_id()          # SAVE THIS, at submit time
job.status()          # QUEUED / RUNNING / DONE / ERROR / CANCELLED
job = service.job("cx1a2b...")     # retrieve tomorrow
  • Do not watch the queue. Largest time sink there is.
  • Never cancel and resubmit — you lose your position.
  • least_busy() for iteration; a chosen backend for results.
  • Record backend, layout, score, job id, and date — calibration drifts.
Mode Behavior Use for
Job queues independently one-offs
Batch submitted as a group independent circuits
Session keeps queue position between iterations VQE/QAOA loops

Sessions are metered while open, including while your optimizer thinks.

★★★ Noise or bug? The decision procedure

  1. DID IT RUN WHERE YOU THINK?   backend.name, final_index_layout(), preflight
  2. DOES IT FAIL IN SIMULATION TOO?
        fails there too -> IT IS A BUG. STOP.
  3. HOW FAR FROM THE NOISELESS REFERENCE?
        ratio ~1.0 -> noise      ratio ~0.5 or ~0 -> structural
  4. IS THE DEVIATION THE RIGHT SIZE?  (error budget from calibration data)
        within 2x -> noise, EXPLAINED        10x larger -> routing/layout
  5. WHAT SHAPE IS THE DEVIATION?  (Ch. 11 two axes)
        impossible outcomes + balanced -> depolarizing/readout
        no impossible outcomes + tilted -> T1
        neither, still wrong -> MEASURE ANOTHER BASIS

Steps 1 and 2 are free and resolve most cases. They are the two people skip.

A failure that survives the removal of noise is not a noise failure. One line, airtight inference. Four bugs so far in this book (Ch. 7 layout trap, Ch. 8 parameter ordering, Ch. 8 CS1's four physics hypotheses, Ch. 10 routing) would each have been caught in seconds by it.

Check the pipeline before the physics.

Three failures that looked alike (CS2)

correct diagnosed by verdict
A 0.9727 step 4 — budget predicted 0.9706 noise, explained
B 0.2844 step 1 — preflight bad layout
C 0.4897 step 2 — noiseless sim BUG

The worst-looking run was easiest; the mildest-looking one was the only bug.

  • Run B: only 4 of 8 outcomes appeared, all with the middle bit set — that bit reads stuck qubit 84.
  • Run C: two clean peaks, 000 and 011 — not 111. 111 appeared 5 times in 4096.

A tall peak on a single WRONG outcome is never noise. Noise spreads. One tall wrong peak is deterministic — it lives in your circuit, layout, or bit ordering.

Read bit positions, not just totals. Both histograms named their own fault; both scalar summaries discarded it.

Common pitfalls

  • Choosing a backend by qubit count.
  • Thresholding on the averaged readout error (passes a stuck qubit at 0.5).
  • Assuming "the device noise model" is one thing.
  • Watching the queue; cancelling and resubmitting.
  • Reaching for physics before running the noiseless simulation.
  • Continuing down the procedure after an early step gave a clean answer (Run B's error budget would have misled).
  • Reporting a result without the layout and the date.

Project piece added this chapter

vqelab/backends.py v3device_health(), best_layout(), preflight() / require_preflight(), readout_directions(), and RunRecord extended with job id, layout, score, and date. 8 tests, all passing, including one asserting that best_layout() always passes its own preflight.