Repository navigation
QFw: measure Braket programs in qubit order so every backend keys alike - #149
Merged
Merged
Conversation
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>
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
to_braket_qasm3now 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, andremap_countsrebuilds 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) reads100on SV1 but read001on 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:
measuredQubits[0, 1, 2](by qubit index)100[2, 1, 0](by classical register)001Both are self-consistent, but
amazon-braket-qdmi1.2.0 (and main) builds its histogram keys by reversing each row as it comes, without readingmeasuredQubits(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.{'1': 10}; asymmetric circuit{'100': 100}; Qiskit GHZ (1024 shots) viaQFwBackend(provider="shim"){'000': 547, '111': 477}.{'100': 77, '000': 19, '110': 3, '010': 1}, where the same circuit read{'001': 83, ...}before this change.Notes
remap_counts(one qubit may fill several bits, last write wins), so the two should compose.