Skip to content

QFw: measure Braket programs in qubit order so every backend keys alike - #149

Merged
DougSO merged 1 commit into
mainfrom
braket-canonical-measurements
Oct 15, 2026
Merged

DougSO merged 1 commit into
mainfrom
braket-canonical-measurements

Conversation

@DougSO

@DougSO DougSO commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

What

to_braket_qasm3 now writes every measurement as one block at the end of the program, c[k] = measure q[i_k] in ascending qubit order, into a register of exactly the measured qubits. The map it records still carries the circuit's own classical bits, and remap_counts rebuilds the Qiskit key from it as before, now filling every bit a qubit was measured into (the last measurement into a bit wins). A gate on a qubit after its measurement is refused before a task is created, since Braket takes terminal measurements only.

Why

The first permuted-measurement circuit run on a real Braket QPU came back wrong. The shim smoke's asymmetric circuit (X on q0, measured q2->c0, q0->c2, q1->c1) reads 100 on SV1 but read 001 on Rigetti Cepheus-1-108Q, for the same OpenQASM 3 program.

The raw task results explain it. Braket's backends do not agree on the order of a result's columns:

device measuredQubits row for the ideal shot
SV1 [0, 1, 2] (by qubit index) 100
Cepheus-1-108Q [2, 1, 0] (by classical register) 001

Both are self-consistent, but amazon-braket-qdmi 1.2.0 (and main) builds its histogram keys by reversing each row as it comes, without reading measuredQubits (DeviceParser.cpp, parseMeasurementResults). So the key is "highest measured qubit leftmost" on SV1 and "highest classical bit leftmost" on Rigetti, and no fixed remap can serve both.

Making bit order, statement order and qubit order one and the same removes the ambiguity: every backend returns the same rows, the library's key reads highest measured qubit leftmost everywhere, and the existing remap is right. This also stays right if the library moves to classical-output semantics (munich-quantum-software/amazon-braket-qdmi-device#242): in the rank register, classical order and qubit order are the same order.

Verified

  • tests/qiskit/test_braket_qasm3.py (12 passed, real Qiskit), tests/mock (631 passed, 1 skipped), ci-syntax clean.
  • Live, through the shim QPM bound to each device, with this change installed:
    • SV1: X circuit {'1': 10}; asymmetric circuit {'100': 100}; Qiskit GHZ (1024 shots) via QFwBackend(provider="shim") {'000': 547, '111': 477}.
    • Rigetti Cepheus-1-108Q: asymmetric circuit, 100 shots, {'100': 77, '000': 19, '110': 3, '010': 1}, where the same circuit read {'001': 83, ...} before this change.

Notes

The first permuted-measurement circuit run on a real Braket QPU came back
wrong. The shim smoke's asymmetric circuit (X on q0, measured q2->c0,
q0->c2, q1->c1) reads "100" on SV1 but read "001" on Rigetti
Cepheus-1-108Q, for the same OpenQASM 3 program.

The raw task results explain it. Braket's backends do not agree on the
order of a result's columns: SV1 reports measuredQubits [0, 1, 2], by
qubit index, and the ideal shot as the row 100; Cepheus reports
[2, 1, 0], by classical register, and the row 001. Both are
self-consistent, but amazon-braket-qdmi 1.2.0 (and main) builds its
histogram keys by reversing each row as it comes, without reading
measuredQubits (DeviceParser.cpp, parseMeasurementResults). So the key
is "highest measured qubit leftmost" on SV1 and "highest classical bit
leftmost" on Rigetti, and no fixed remap serves both.

Make the three orders one. to_braket_qasm3 now writes every measurement
as one block at the end of the program, c[k] = measure q[i_k] in
ascending qubit order, into a register of exactly the measured qubits.
Bit order, statement order and qubit order then coincide, every backend
returns the same rows, and the library's key reads highest measured
qubit leftmost everywhere, which is what remap_counts already undoes.
The map still carries the circuit's own classical bits; remap_counts now
fills every bit a qubit was measured into, and the last measurement into
a bit wins, as in Qiskit. A gate on a qubit after its measurement is
refused before a task is created, since Braket takes terminal
measurements only. This stays right if the library moves to
classical-output semantics (amazon-braket-qdmi-device#242): in the rank
register, classical order and qubit order are the same order.

Verified live through the shim QPM with the change installed: SV1 gives
{'1': 10} for the X circuit, {'100': 100} for the asymmetric circuit and
{'000': 547, '111': 477} for the Qiskit GHZ through QFwBackend; Cepheus
gives {'100': 77, '000': 19, '110': 3, '010': 1} for the asymmetric
circuit at 100 shots, where it read '001' before. tests/qiskit 12
passed, tests/mock 631 passed and 1 skipped, ci-syntax clean.

Signed-off-by: Doug Oucharek <dougso@me.com>
@DougSO
DougSO merged commit 2785270 into main Oct 15, 2026
6 checks passed
@DougSO
DougSO deleted the braket-canonical-measurements branch October 15, 2026 00:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant