You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
reverse_qubit_order() KeyError on aliased operand nodes (same root cause as #331) #333
QasmModule.reverse_qubit_order() raises KeyError on any program whose unrolled AST contains aliased operand nodes — the same root cause as #331, surfacing one method below the fix in #332.
Found during adversarial review of #332: the visited-set guard added there fixes _remap_qubits, but reverse_qubit_order (src/pyqasm/modules/base.py:533-555) walks the same statements and mutates bit.indices[0][0].value in place with a negative-marker trick. On a node shared by k statements, the second visit reads the already-negated value and looks up new_qubit_mappings[reg][-2] → KeyError: -1.
Root cause: have unroll() / the decomposer emit fresh (deep-copied) operand nodes so no AST node is shared across statements. This retires the whole class of bug — every in-place index mutation in modules/base.py is currently exposed to aliasing.
Point fix: apply the same visited-node-id guard from fix: remove_idle_qubits KeyError on shared operand nodes #332 to both passes in reverse_qubit_order. Note the marker pass and the unmark pass each need their own guard (or the method should be rewritten to build the mapping without the negative-marker trick, which only exists to survive double visits it doesn't actually survive).
Option 1 is the better long-term fix; if we take it, the guard added in #332 becomes harmless belt-and-braces.
Root-cause fix is up in #335 (deep-copy operand nodes at the statement constructors in maps/gates.py + Decomposer), so no need to pick this one up after all — reviews welcome there instead. It also independently resolves the #331 repro; the guard from #332 stays as belt-and-braces.
Activity
ryanhill1 commentedon Jul 24, 2026
Root-cause fix is up in #335 (deep-copy operand nodes at the statement constructors in
maps/gates.py+Decomposer), so no need to pick this one up after all — reviews welcome there instead. It also independently resolves the #331 repro; the guard from #332 stays as belt-and-braces.fix: emit fresh operand nodes from gate decompositions (qBraid#335)