From df4b452c7cfe16d2864bd307598d1e09c95d1b7e Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Mon, 1 Jul 2024 16:45:52 +0200 Subject: [PATCH] Fix `replace_block_with_op` on operations with wrong number of qubits (#12637) * fix illegal op insertion * rm dangling print * fix PauliEvolution * Update qiskit/dagcircuit/dagcircuit.py Co-authored-by: John Lapeyre --------- Co-authored-by: John Lapeyre (cherry picked from commit 67fd35a2eacf977f0cd2539b7ae5b4c4210f50bf) --- qiskit/dagcircuit/dagcircuit.py | 7 +++++++ .../pauli_2q_evolution_commutation.py | 6 +++++- ...n-illegal-replace-block-50cef8da757a580a.yaml | 7 +++++++ test/python/dagcircuit/test_dagcircuit.py | 16 ++++++++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/raise-on-illegal-replace-block-50cef8da757a580a.yaml diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 86520321d379..9f45e388f73c 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1173,6 +1173,13 @@ def replace_block_with_op(self, node_block, op, wire_pos_map, cycle_check=True): block_cargs.sort(key=wire_pos_map.get) new_node = DAGOpNode(op, block_qargs, block_cargs, dag=self) + # check the op to insert matches the number of qubits we put it on + if op.num_qubits != len(block_qargs): + raise DAGCircuitError( + f"Number of qubits in the replacement operation ({op.num_qubits}) is not equal to " + f"the number of qubits in the block ({len(block_qargs)})!" + ) + try: new_node._node_id = self._multi_graph.contract_nodes( block_ids, new_node, check_cycle=cycle_check diff --git a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py index 8b92c5cdaf53..bb78762d3f63 100644 --- a/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py +++ b/qiskit/transpiler/passes/routing/commuting_2q_gate_routing/pauli_2q_evolution_commutation.py @@ -51,7 +51,11 @@ def run(self, dag: DAGCircuit) -> DAGCircuit: sub_dag = self._decompose_to_2q(dag, node.op) block_op = Commuting2qBlock(set(sub_dag.op_nodes())) - wire_order = {wire: idx for idx, wire in enumerate(dag.qubits)} + wire_order = { + wire: idx + for idx, wire in enumerate(sub_dag.qubits) + if wire not in sub_dag.idle_wires() + } dag.replace_block_with_op([node], block_op, wire_order) return dag diff --git a/releasenotes/notes/raise-on-illegal-replace-block-50cef8da757a580a.yaml b/releasenotes/notes/raise-on-illegal-replace-block-50cef8da757a580a.yaml new file mode 100644 index 000000000000..f4971fe520a0 --- /dev/null +++ b/releasenotes/notes/raise-on-illegal-replace-block-50cef8da757a580a.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Previously, :meth:`.DAGCircuit.replace_block_with_op` allowed to place an + ``n``-qubit operation onto a block of ``m`` qubits, leaving the DAG in an + invalid state. This behavior has been fixed, and the attempt will raise + a :class:`.DAGCircuitError`. diff --git a/test/python/dagcircuit/test_dagcircuit.py b/test/python/dagcircuit/test_dagcircuit.py index 9d73ac5a1af0..8ef4730ba3ff 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -2462,6 +2462,22 @@ def test_single_node_block(self): self.assertEqual(expected_dag.count_ops(), dag.count_ops()) self.assertIsInstance(new_node.op, XGate) + def test_invalid_replacement_size(self): + """Test inserting an operation on a wrong number of qubits raises.""" + + # two X gates, normal circuit + qc = QuantumCircuit(2) + qc.x(range(2)) + + # mutilate the DAG + dag = circuit_to_dag(qc) + to_replace = list(dag.op_nodes()) + new_node = XGate() + idx_map = {node.qargs[0]: i for i, node in enumerate(to_replace)} + + with self.assertRaises(DAGCircuitError): + dag.replace_block_with_op(to_replace, new_node, idx_map) + def test_replace_control_flow_block(self): """Test that we can replace a block of control-flow nodes with a single one.""" body = QuantumCircuit(1)