Repository navigation
fix: Record measurements into named classical registers - #409
yitchen-tim wants to merge 5 commits into
Conversation
Replace the flat, anonymous classical bit index space in the simulator circuit IR with first-class registers. A measurement is now recorded as "qubit q into element e of register r" instead of a global classical index, and the interpreter carries register identity from its scoped symbol tables down to the circuit.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #409 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 49 49
Lines 4964 5030 +66
Branches 580 580
=========================================
+ Hits 4964 5030 +66 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| @@ -2597,13 +2597,14 @@ def test_simple_conditional_feedforward(self, simulator): | |||
| """MCM with conditional: if measured 1, flip second qubit.""" | |||
| qasm = """ | |||
There was a problem hiding this comment.
In this test file, many tests have measurements become fully specified. The partial measurements is covered in test_scalar_bit_mcm_reports_only_the_measured_qubit.
There was a problem hiding this comment.
Also, storing measurements into multiple registers becomes not allowed until the output is supported (we have PR opened: #399). Users must store measurement into a single register for now. I have changed few tests so that there are only one register for measurements.
| def _apply_pending_mcm_targets(self) -> None: | ||
| """Apply every pending measurement now, in program order. | ||
|
|
||
| Called when something in the program needs a deferred measurement to have |
There was a problem hiding this comment.
I keep these docstring to give context about where and when these private functions are called. I feel they can be tricky to understand.
|
@speller26 This is ready for review. This PR
|
Issue #, if available: Simplifies #399 (output variables, estimated removing 600 LOC) and future PRs related to classical control
Description of changes:
Replace the flat, anonymous classical bit index space in the simulator's circuit IR with named classical registers. A measurement is now recorded as "qubit q into element e of register r" rather than into a global classical index. Existing single register programs produce identical results. Six result bugs in the measurement path are fixed, and programs whose measurements span more than one register are rejected until per register results can be reported through the
outputsfield.Background
Circuitkept two parallel lists,measured_qubitsandtarget_classical_indices, and the simulator sorted the indices to get column order. The classical index was inherited from JAQCD, where a program has exactly one classical register, but OpenQASM programs declare many and the interpreter already resolves them through scoped symbol tables. Dropping that identity at the circuit boundary caused several bugs:c[1] = measure q[0]; d[1] = measure q[1];collided on index 1 and produced one column.bit bdestination was never recorded in the circuit when the program branched, someasurementsfell back to every qubit and reported unmeasured qubits.bit bandbit[1] bdisagreed on column count.bit[4] c; c[3] = measure q;sorted against unrelated registers).shots=0crashed.The branched flow also stored each mid circuit outcome three times under two addressing schemes (per path variable,
circuit.target_classical_indices,path._mcm_outcomesby flat index), reconciled by hand. Theoutputfeature in #399 had to rebuild the variable to column mapping after the fact with name keyed maps, which reintroduced scope shadowing problems.Changes
Key changes are:
Circuitrecords measurements inClassicalRegisterobjects with per element qubit sources, replacing the two parallel listsmeasured_qubitsandtarget_classical_indicesthat addressed every bit by a single flat index.RegisterTableon the context maps eachbitdeclaration to its register handle, so destinations are resolved by scope once and no name string or flat index reaches the circuit.Other changes are:
measurement_slotsdefines the result columns as registers in declaration order and elements in index order, andrun_openqasmand_run_branchedbuildmeasurementsfrom it instead of sorting classical indices._record_measurement_outcomes, and per path outcomes are keyed by(register, element)rather than flat index.outputfeature needs.bitandbit[1]are both size one registers, so a scalar destination in branched mode now produces one column instead of falling back to every qubit.outputs, while the internals stay multi register throughout.Useful concepts and terms for reviewing
(register, element, qubit)triple per bound element, listed in declaration order then element order;measurement_slotsis the definition of the columns inmeasurements.measuredQubits. Crucially, this is not the same as "qubits that are measured" because there could be measured qubits that are not associated with a bitstring.ClassicalRegisterhandle, so a shadowingbit bin an inner scope resolves to a different register than the outer one.LOC summary