Fix #34: reject gates and repeated measurements on measured qubits while building - #92
Open
corgab wants to merge 3 commits into
Open
Fix #34: reject gates and repeated measurements on measured qubits while building#92corgab wants to merge 3 commits into
corgab wants to merge 3 commits into
Conversation
…building Braket refuses a measure() that lists the same qubit twice and any instruction applied to a qubit that was already measured, but the builder only checked that indices were in range, so both mistakes surfaced from the Python subprocess as a generic execution error. CircuitBuilder now tracks the measured qubits (every qubit for a measure-all) and throws InvalidCircuitException at build time, including for appended fragments and definitions rebuilt with fromArray(), with messages naming the gate and the qubit. Closes #34
…guard append() drops a fragment's measurements by contract, so they must not mark the parent's qubits as measured; only the fragment's gates are checked against the parent's own measurements. A measure-all now sets a flag that covers qubits added by a later qubits() call, matching how the Python side expands it against the final qubit count, and push() resolves the gate name and indices once for both checks.
PHPStan reads Gate::qubitIndices() as array<int>, so the guard's parameter uses the same shape instead of list<int>.
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.
Summary
Braket's
Circuitrejects ameasure()that lists the same qubit twice (cannot repeat qubit(s) 0 in the same measurement) and any instruction applied to a qubit that was already measured (cannot apply instruction to measured qubits).CircuitBuilderonly checked that indices were in range, so->measure([0, 0])and->measure(0)->h(0)built fine in PHP and failed one subprocess later as a genericQuantumExecutionException::fromPythonError(), instead of theInvalidCircuitExceptionevery other shape mistake produces.Plan
Track the measured qubits in the builder and check every pushed gate against them, so both mistakes fail at build time with a specific message, including for fragments added with
append()and definitions rebuilt withfromArray(), which also go through the same check. Respect the two existing contracts:append()drops the fragment's measurements, andqubits()may grow the circuit after a measure-all.Changes
CircuitBuilder: a$measuredQubitsset plus a$measuredAllflag, and a privateassertMeasurementOrder(Gate, string $name, array $indices)called frompush()(with the name and indices resolved once for both the range check and this one) and, for non-measurement gates only, from theappend()loop. An explicit measurement records its targets; a measure-all sets the flag, so qubits added by a laterqubits()call count as measured too, matching howcommon.pyexpandstargets: nullagainst the final qubit count. A repeated target within one call throwsrepeatedMeasurementTarget(); a target already measured, a second measure-all, or a gate touching a measured qubit throwsqubitAlreadyMeasured().InvalidCircuitException::repeatedMeasurementTarget(int $qubit)andqubitAlreadyMeasured(string $gate, int $qubit): messages name the qubit and the gate, and say to move the measurement to the end.append()drops them.No config change, no new dependencies, no change for circuits that already ran on Braket (they could not contain either pattern).
Tests
CircuitBuilderTest: duplicate target in onemeasure(); measuring again (same target, inside a wider measurement, measure-all afterwards); a single-qubit gate, and a two-qubit gate through its control or its target, on a measured qubit; any gate after a measure-all; gates on unmeasured qubits still allowed; an appended fragment touching a measured qubit; appending a fragment that ends inmeasure()and then measuring the parent still works; a qubit added after a measure-all is measured too; a second measurement after a measure-all; afromArray()definition acting on a measured qubit. All the rejecting cases built without error onmain.Local: pint passed, pest 805 tests passed.
Closes #34