From f72590a01b0fe2a04a3fa2d6a782f7320536b84d Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Fri, 21 Jun 2024 15:43:06 +0200 Subject: [PATCH 1/4] fix illegal op insertion --- qiskit/dagcircuit/dagcircuit.py | 10 ++++++++++ ...n-illegal-replace-block-50cef8da757a580a.yaml | 7 +++++++ test/python/dagcircuit/test_dagcircuit.py | 16 ++++++++++++++++ 3 files changed, 33 insertions(+) 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 d14340a8cb9b..79bf4126549f 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1336,6 +1336,16 @@ def replace_block_with_op( else: block_cargs.update(node_resources(nd.op.target).clbits) + # check the op to insert matches the number of qubits we put it on + print("replacing", op.num_qubits, "on", len(block_qargs)) + print(op) + print() + if op.num_qubits != len(block_qargs): + raise DAGCircuitError( + f"Number of qubits in the replacement operation ({op.num_qubits}) does not match " + f"the number of qubits ({len(block_qargs)})!" + ) + block_qargs = [bit for bit in block_qargs if bit in wire_pos_map] block_qargs.sort(key=wire_pos_map.get) block_cargs = [bit for bit in block_cargs if bit in wire_pos_map] 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 4ab4e392cbb1..042ddf46b128 100644 --- a/test/python/dagcircuit/test_dagcircuit.py +++ b/test/python/dagcircuit/test_dagcircuit.py @@ -2903,6 +2903,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) From 4291b9bbe3e9c0f1a7099f8055b9b9a14e4cc0bd Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Fri, 21 Jun 2024 15:46:25 +0200 Subject: [PATCH 2/4] rm dangling print --- qiskit/dagcircuit/dagcircuit.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 79bf4126549f..8c4b36d0847c 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1337,9 +1337,6 @@ def replace_block_with_op( block_cargs.update(node_resources(nd.op.target).clbits) # check the op to insert matches the number of qubits we put it on - print("replacing", op.num_qubits, "on", len(block_qargs)) - print(op) - print() if op.num_qubits != len(block_qargs): raise DAGCircuitError( f"Number of qubits in the replacement operation ({op.num_qubits}) does not match " From 4388d40895883daf5696ccd37c774b1a4257435f Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Fri, 21 Jun 2024 16:41:21 +0200 Subject: [PATCH 3/4] fix PauliEvolution --- qiskit/dagcircuit/dagcircuit.py | 12 ++++++------ .../pauli_2q_evolution_commutation.py | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 8c4b36d0847c..8d4d9b30501a 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1336,6 +1336,12 @@ def replace_block_with_op( else: block_cargs.update(node_resources(nd.op.target).clbits) + block_qargs = [bit for bit in block_qargs if bit in wire_pos_map] + block_qargs.sort(key=wire_pos_map.get) + block_cargs = [bit for bit in block_cargs if bit in wire_pos_map] + 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( @@ -1343,12 +1349,6 @@ def replace_block_with_op( f"the number of qubits ({len(block_qargs)})!" ) - block_qargs = [bit for bit in block_qargs if bit in wire_pos_map] - block_qargs.sort(key=wire_pos_map.get) - block_cargs = [bit for bit in block_cargs if bit in wire_pos_map] - block_cargs.sort(key=wire_pos_map.get) - new_node = DAGOpNode(op, block_qargs, block_cargs, dag=self) - 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 641b40c9f3e1..beadc884465b 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 From 5435a87516aad94775ae86f9d0b7e1c9b06a3085 Mon Sep 17 00:00:00 2001 From: Julien Gacon Date: Mon, 24 Jun 2024 16:37:40 +0200 Subject: [PATCH 4/4] Update qiskit/dagcircuit/dagcircuit.py Co-authored-by: John Lapeyre --- qiskit/dagcircuit/dagcircuit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 8d4d9b30501a..8706fad77b2b 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1345,8 +1345,8 @@ def replace_block_with_op( # 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}) does not match " - f"the number of qubits ({len(block_qargs)})!" + 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: