Resolve PHIR JSON qubit arguments through declaration-order register ranges - #887
Conversation
…ranges instead of the bare index
645d057 to
b0c6938
Compare
|
Updated to It found one regression this PR had introduced: a That review also settled, by building and running rather than reading, the question the first arm could not: the wrap hazard is genuinely closed in release. It swept allocation budgets 0–14 through declarations, measurement returns, combining and export (0–10 error, 11–14 succeed with ids outside the reserved range) and confirmed Verification on the rebased tree: |
|
Fixed in #851 added the The site now collects into My earlier verification missed it because I ran
Branch is level with |
Closes #819.
Problem
The Rust PHIR JSON layers resolved a qubit argument
[register, index]to the bare index and discarded the register name, so in any program with more than one qubit register every gate after the first register targeted the wrong qubit. Verified at the IR layer ondevbefore the fix, for a program declaringa[2]thenb[2]:b[0]anda[0]resolved to the same SSA value, so the second register aliased the first.a[1]was right only becauseais declared first.Change
Environmentnow owns declaration-order global qubit ranges (add_quantum_register,resolve_qubitwith an explicit out-of-bounds error), and every qubit-argument resolution goes through it:phir_converter.rs),operations.rs),qvar_metaduplicate of the same bookkeeping (classical_interpreter.rs).The numbering matches the authoritative Python path, which is correct today: each
qvar_definetakes the next contiguous block in declaration order (pyphir.py~306) and an argument resolves asqvar_meta[qsym].qubit_ids[qid](~248). The converter reserves SSA ids0..num_qubitsfor qubits and allocates every later id through one checked allocator that errors on exhaustion.Validation tightened where the converter previously accepted malformed input: a
qvar_definewith nosizeor a non-qubitdata_typeis rejected (the spec requiressize; the Python reference raises on the type), and a duplicate declaration is refused. That last one is a deliberate divergence — Python allocates a second block and overwrites the name — and is commented as such at the rejection.Found along the way, filed separately
Xbefore aMeasurereads 0) and ten of its quantum assertions are vacuous. That is why this defect survived, and it is why the tests here observe at the IR and command layer instead.m[1]builds 1 instead of 2.Tests
Eight-plus regressions in
tests/qubit_register_resolution.rs, all at the IR or command layer:a[2] b[2]equals the module for one flatq[4]with hand-computed indices, repeated for uneven sizesx[1] y[3] z[2]and forzdeclared beforeaso declaration order cannot silently become alphabetical order;Mutation-checked: resolving to the bare index fails 7 of 8 addressing tests including both oracles; making the SSA allocator wrap instead of checking fails all three exhaustion tests.
Verification
cargo test -p pecos-phir-json -p pecos-phir -p pecos-qasm --no-fail-fast(966 passed, 0 failed),cargo clippy --lockedon those three crates with--all-targets -- -D warningsrun cold after touching the changed files, andcargo fmt --check.How this was produced
Implemented by OpenAI
gpt-6-astra(Codex CLI 0.154.0) from a task packet written by Claude Fable 5.1 in Claude Code; Claude reproduced the defect itself first, reviewed every hunk, re-ran all verification and both mutation checks, and sent findings back for a fix round. An independent Codex review arm, blind to the implementation session, found the SSA-overflow defect that the fix round then closed; every finding was reproduced before being acted on. Posted at the maintainer's request.