Case Study 2: The Abstraction That Hid the Decision
The promise
A single API over every quantum computer. Write your circuit once, change one line, run it anywhere:
device = LocalSimulator() # development
device = AwsDevice("arn:aws:braket:::device/...") # superconducting
device = AwsDevice("arn:aws:braket:::device/...") # trapped ion
device = AwsDevice("arn:aws:braket:::device/...") # neutral atom
This is a genuinely good abstraction, and the promise is largely kept. The circuit construction is identical. The result types are identical. The workflow is identical. A Bell state built once runs on all of them and produces the same distribution.
And that is precisely the problem.
What the abstraction preserves
Verified across the three frameworks this book has now used, and across Braket's own backends:
| Property | Transfers? |
|---|---|
| Circuit construction | yes |
Result types (probability, expectation, …) |
yes |
| Noise channel semantics | yes — Chapter 11's signature table reproduced exactly |
| Correctness of the output distribution | yes |
| The cost of running it | no |
| Which failure modes exist | no |
| Whether it is feasible at all | no |
The noise reproduction is worth showing, because it demonstrates how much genuinely does transfer:
channel error fraction imbalance
depolarizing 0.05 0.0630 +0.0042
bit flip 0.05 0.0930 +0.0008
phase flip 0.05 0.0000 +0.0012
amplitude damping 0.15 0.1073 +0.3228
phase damping 0.30 0.0000 -0.0068
This is Chapter 11 §11.7's table, in a third framework, unchanged. Depolarizing and bit flip produce impossible outcomes with balanced peaks; amplitude damping tilts the peaks (+0.32) without producing many impossible outcomes; phase damping and phase flip are invisible.
That last row is the same finding from Chapter 11 (Aer), Chapter 14 (Cirq), and now Braket. The computational basis is blind to phase in every framework, because it is a fact about measurement rather than about software. Physics transfers perfectly.
What it hides
Everything about whether your program is a good idea.
It hides a 3.18× cost multiplier
Case Study 1's star circuit costs 11 two-qubit gates on all-to-all hardware and 35 on heavy-hex.
Nothing in the Braket API surfaces this. device.run(circuit, shots=1000) returns counts either
way. The circuit is valid, the results are correct, and one of them cost three times as much and had
seven times the depth.
It hides which failure modes exist
Chapter 12 built an entire apparatus around superconducting hardware's problems:
- a 288× spread in two-qubit gate error across one chip,
- twelve qubits with readout error above 10%,
- nine dead gate pairs,
- and qubit 84, stuck at a constant output.
None of that exists on a trapped-ion machine. Every ion of a species is identical by the laws of
physics; there is no fabrication lottery. device_health(), best_layout(), and preflight() are
answers to a question ion traps do not pose.
Conversely, ion traps have their own constraints — gate speed, chain length limits, and mode heating — that superconducting devices do not. The portable API describes neither.
It hides feasibility
A variational workload running thousands of circuits (Chapter 16 §16.4: $2n+1$ executions per gradient, per iteration) is dominated by wall-clock time. Ion gates are ~100× slower. A workload that is comfortable on superconducting hardware may be simply impractical on ions — not slower, but not worth attempting — and the one-line device swap gives no hint.
The pattern
This is a specific instance of a general failure, and naming it is the point of the case study.
An abstraction that unifies an interface implies that the things behind it are interchangeable. When they are not, the abstraction is making an argument, and the argument is wrong.
It has appeared before in this book, at every level:
| Level | Abstraction | What it hid |
|---|---|---|
| A number | readout_error = 0.5 (Ch. 12) |
stuck-at-1 versus a coin flip |
| A pass | dynamical_decoupling.enable = True (Ch. 13) |
that it inserted 4 gates and did nothing |
| A framework | counts = {'01': ...} (Ch. 14) |
which qubit is which bit |
| A device swap | AwsDevice(...) (here) |
a 3.18× cost and a different failure catalogue |
In every case the abstraction was correct and insufficient. None of them lied. Each answered the question it was asked and silently declined to mention the question you should have asked.
The response
Not to abandon the abstraction — it is genuinely useful, and rewriting circuits per vendor would be much worse.
The response is to make the hidden decision explicit and cheap. vqelab/topology.py does exactly
this: connectivity_overhead() transpiles against the target and against all-to-all and reports the
ratio; breakeven_error_rate() turns that into the error threshold an alternative modality would need
to beat; and recommend_modality() returns an answer that is required to cite a number.
That last constraint is deliberate, and one of the module's tests enforces it:
def test_a_recommendation_always_carries_its_evidence():
for circuit in (chain_ghz(8), star_ghz(8)):
rec = recommend_modality(circuit)
assert rec.reason, "a recommendation must state why"
assert any(ch.isdigit() for ch in rec.reason), (
"the reason must cite a measurement, not just an opinion")
A recommendation without a measurement behind it is exactly the thing this case study is about — a confident interface over an unexamined choice.
The lessons
Portability of code is not portability of results. The circuit runs everywhere and performs very differently, and only one of those facts is visible in the API.
Physics transfers; economics does not. Chapter 11's noise signatures reproduce exactly across three frameworks and two modalities, because they are facts about measurement. Cost, feasibility, and failure modes are facts about machines, and they do not transfer at all.
When an interface makes two things look interchangeable, check whether they are. The uniformity of the API is a design choice, not evidence about the hardware behind it.
Make the hidden decision explicit, and require evidence. A measurement that takes one transpilation
should not be optional at the point where the choice is made. Build it into the tool, not the
discipline of the user — Chapter 16's converged flag is the same move, for the same reason.
And the recurring theme, now at the architectural level: the abstraction answered the question it was asked. Chapter 12's averaged statistic, Chapter 13's enabled-but-inert pass, Chapter 14's symmetric test, and now a device swap. The failures in this book are rarely wrong answers. They are correct answers to the wrong question, and the skill being built across these chapters is noticing which question you actually asked.
Reproduce it: code/example-04-noise-and-modalities.py for the noise reproduction and the modality
table; code/project-checkpoint.py for the tooling that makes the decision explicit.