-
Notifications
You must be signed in to change notification settings - Fork 27
fix: emit distinct x gates with fresh operands in negctrl expansion #357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4e60a7c
5de9e7d
624ec7c
32e1527
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -228,6 +228,13 @@ def _assert_no_shared_operand_nodes(module): | |
| "c4x q[0], q[1], q[2], q[3], q[4];", | ||
| "ecr q[0], q[1];", | ||
| "inv @ crz(0.5) q[1], q[2];", | ||
| "negctrl @ x q[0], q[1];", | ||
| "negctrl(2) @ x q[0], q[1], q[2];", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type: Maintenance Rationale: These two rows cover the plain Change Requested: Optional. Consider adding two or three of the above as parametrize rows — one line each, no new helper needed.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed on making the matrix the durable guard rather than adding a bespoke test per incident. Added three rows in 32e1527: I checked each against I skipped the broadcast-register and gate-body/for-body shapes: the parametrize fixture is a single operation against one |
||
| # each of these reaches the negctrl expansion through a different | ||
| # operand shape or repeats it, so each is a distinct way to regress | ||
| "negctrl @ x q[{0, 1}], q[2];", | ||
| "pow(2) @ negctrl @ x q[0], q[1];", | ||
| "ctrl @ negctrl @ x q[0], q[1], q[2];", | ||
| ], | ||
| ) | ||
| def test_unroll_emits_fresh_operand_nodes(operation): | ||
|
|
@@ -244,6 +251,89 @@ def test_unroll_emits_fresh_operand_nodes(operation): | |
| _assert_no_shared_operand_nodes(module) | ||
|
|
||
|
|
||
| def test_reverse_qubit_order_negctrl(): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Type: Implementation Rationale: This test covers one of the three consumers the aliasing broke. Issue #350 names Change Requested: Add two short cases alongside this one, so all three passes the aliasing reached are pinned:
Both expected outputs were confirmed against this commit.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both added in 32e1527, with your expected outputs confirmed locally.
One correction to the expected consolidated output: the register declaration is emitted before the include, so it is |
||
| """Test reverse_qubit_order on a negctrl gate whose leading and trailing x | ||
| statements previously were the same object (issue #350)""" | ||
| qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| include "stdgates.inc"; | ||
| qubit[3] q; | ||
| negctrl @ x q[0], q[1]; | ||
| """ | ||
|
|
||
| expected_qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| include "stdgates.inc"; | ||
| qubit[3] q; | ||
| x q[2]; | ||
| cx q[2], q[1]; | ||
| x q[2]; | ||
| """ | ||
|
|
||
| module = loads(qasm3_str) | ||
| module.unroll() | ||
| module.reverse_qubit_order() | ||
| check_unrolled_qasm(dumps(module), expected_qasm3_str) | ||
|
|
||
|
|
||
| def test_remove_idle_qubits_negctrl(): | ||
| """Test remove_idle_qubits on a negctrl gate whose leading and trailing x | ||
| statements previously were the same object (issue #350). | ||
|
|
||
| This path survived the aliasing only via the ``visited_node_ids`` guard in | ||
| ``_remap_qubits``, so it is pinned here to keep that incidental rescue from | ||
| being the only thing standing between a refactor and the crash. | ||
| """ | ||
| qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| include "stdgates.inc"; | ||
| qubit[3] q; | ||
| negctrl @ x q[0], q[1]; | ||
| """ | ||
|
|
||
| expected_qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| include "stdgates.inc"; | ||
| qubit[2] q; | ||
| x q[0]; | ||
| cx q[0], q[1]; | ||
| x q[0]; | ||
| """ | ||
|
|
||
| module = loads(qasm3_str) | ||
| module.unroll() | ||
| module.remove_idle_qubits() | ||
| check_unrolled_qasm(dumps(module), expected_qasm3_str) | ||
|
|
||
|
|
||
| def test_consolidate_qubits_negctrl(): | ||
| """Test unroll(consolidate_qubits=True) on a negctrl gate whose leading and | ||
| trailing x statements previously were the same object (issue #350). | ||
|
|
||
| Consolidation rewrote the shared operand once, then met the already-renamed | ||
| node again and raised ``KeyError: '__PYQASM_QUBITS__'``. | ||
| """ | ||
| qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| include "stdgates.inc"; | ||
| qubit[3] q; | ||
| negctrl @ x q[0], q[1]; | ||
| """ | ||
|
|
||
| expected_qasm3_str = """ | ||
| OPENQASM 3.0; | ||
| qubit[3] __PYQASM_QUBITS__; | ||
| include "stdgates.inc"; | ||
| x __PYQASM_QUBITS__[0]; | ||
| cx __PYQASM_QUBITS__[0], __PYQASM_QUBITS__[1]; | ||
| x __PYQASM_QUBITS__[0]; | ||
| """ | ||
|
|
||
| module = loads(qasm3_str) | ||
| module.unroll(consolidate_qubits=True) | ||
| check_unrolled_qasm(dumps(module), expected_qasm3_str) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "operation", ["crz(0.5) q[1], q[2];", "swap q[0], q[2];", "cz q[1], q[2];"] | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add a docstring to
_neg_x_gates.The new nested function has no docstring. Add a concise description of its purpose and return value.
As per coding guidelines, every Python function must have a docstring explaining its purpose, parameters, and return values.
Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Coding guidelines