diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 953efe51ecd7..b2614624b415 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -81,6 +81,7 @@ Optimize1qGatesSimpleCommutation RemoveDiagonalGatesBeforeMeasure RemoveResetInZeroState + RemoveFinalReset HoareOptimizer TemplateOptimization EchoRZXWeylDecomposition @@ -225,6 +226,7 @@ from .optimization import Optimize1qGatesSimpleCommutation from .optimization import OptimizeSwapBeforeMeasure from .optimization import RemoveResetInZeroState +from .optimization import RemoveFinalReset from .optimization import RemoveDiagonalGatesBeforeMeasure from .optimization import HoareOptimizer from .optimization import TemplateOptimization diff --git a/qiskit/transpiler/passes/optimization/__init__.py b/qiskit/transpiler/passes/optimization/__init__.py index 84e81bfebdef..40e877ec5147 100644 --- a/qiskit/transpiler/passes/optimization/__init__.py +++ b/qiskit/transpiler/passes/optimization/__init__.py @@ -24,6 +24,7 @@ from .optimize_1q_commutation import Optimize1qGatesSimpleCommutation from .optimize_swap_before_measure import OptimizeSwapBeforeMeasure from .remove_reset_in_zero_state import RemoveResetInZeroState +from .remove_final_reset import RemoveFinalReset from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure from .hoare_opt import HoareOptimizer from .template_optimization import TemplateOptimization diff --git a/qiskit/transpiler/passes/optimization/remove_final_reset.py b/qiskit/transpiler/passes/optimization/remove_final_reset.py new file mode 100644 index 000000000000..bb028508fdfb --- /dev/null +++ b/qiskit/transpiler/passes/optimization/remove_final_reset.py @@ -0,0 +1,37 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Remove reset when it is the final instruction on a qubit.""" + +from qiskit.circuit import Reset, Qubit +from qiskit.dagcircuit import DAGOpNode +from qiskit.transpiler.basepasses import TransformationPass + + +class RemoveFinalReset(TransformationPass): + """Remove reset when it is the final instruction on a qubit wire.""" + + def run(self, dag): + """Run the RemoveFinalReset pass on `dag`. + + Args: + dag (DAGCircuit): the DAG to be optimized. + + Returns: + DAGCircuit: the optimized DAG. + """ + for output_node in dag.output_map.values(): + if isinstance(output_node.wire, Qubit): + pred = next(dag.predecessors(output_node)) + if isinstance(pred, DAGOpNode) and isinstance(pred.op, Reset): + dag.remove_op_node(pred) + return dag diff --git a/releasenotes/notes/remove-final-reset-488247c01c4e147d.yaml b/releasenotes/notes/remove-final-reset-488247c01c4e147d.yaml new file mode 100644 index 000000000000..090654e16b9d --- /dev/null +++ b/releasenotes/notes/remove-final-reset-488247c01c4e147d.yaml @@ -0,0 +1,35 @@ +--- +features: + - | + Added a new transpiler pass, :class:`.RemoveFinalReset`, which + will remove any :class:`.Reset` operation which is the final + instruction on a qubit wire. For example, taking a circuit with + final :class:`.Reset`\ s: + + .. plot:: + + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(3, 1) + qc.reset(0) + qc.h(range(3)) + qc.cx(1, 0) + qc.measure(0, 0) + qc.reset(range(3)) + qc.draw("mpl") + + will remove the final resets when the pass is run: + + .. plot:: + :include-source: + + from qiskit.transpiler.passes import RemoveFinalReset + from qiskit.circuit import QuantumCircuit + + qc = QuantumCircuit(3, 1) + qc.reset(0) + qc.h(range(3)) + qc.cx(1, 0) + qc.measure(0, 0) + qc.reset(range(3)) + RemoveFinalReset()(qc).draw("mpl") diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index e18f483fb959..8e50f26d840d 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -925,6 +925,7 @@ def test_init_resets_kept_preset_passmanagers(self, optimization_level): num_qubits = 5 qc = QuantumCircuit(num_qubits) qc.reset(range(num_qubits)) + qc.h(range(num_qubits)) num_resets = transpile(qc, optimization_level=optimization_level).count_ops()["reset"] self.assertEqual(num_resets, num_qubits) diff --git a/test/python/transpiler/test_remove_final_reset.py b/test/python/transpiler/test_remove_final_reset.py new file mode 100644 index 000000000000..22ac234a81f5 --- /dev/null +++ b/test/python/transpiler/test_remove_final_reset.py @@ -0,0 +1,117 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2023. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test RemoveFinalReset pass""" + +import unittest + +from qiskit import QuantumRegister, QuantumCircuit +from qiskit.passmanager.flow_controllers import DoWhileController +from qiskit.transpiler import PassManager +from qiskit.transpiler.passes import RemoveFinalReset, DAGFixedPoint +from qiskit.converters import circuit_to_dag +from test import QiskitTestCase # pylint: disable=wrong-import-order + + +class TestRemoveFinalReset(QiskitTestCase): + """Test remove-reset-in-zero-state optimizations.""" + + def test_optimize_single_reset(self): + """Remove a single final reset + qr0:--[H]--|0>-- ==> qr0:--[H]-- + """ + qr = QuantumRegister(1, "qr") + circuit = QuantumCircuit(qr) + circuit.h(0) + circuit.reset(qr) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr) + expected.h(0) + + pass_ = RemoveFinalReset() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_dont_optimize_non_final_reset(self): + """Do not remove reset if not final instruction + qr0:--|0>--[H]-- ==> qr0:--|0>--[H]-- + """ + qr = QuantumRegister(1, "qr") + circuit = QuantumCircuit(qr) + circuit.reset(qr) + circuit.h(qr) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr) + expected.reset(qr) + expected.h(qr) + + pass_ = RemoveFinalReset() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_single_reset_in_diff_qubits(self): + """Remove a single final reset in different qubits + qr0:--[H]--|0>-- qr0:--[H]-- + ==> + qr1:--[X]--|0>-- qr1:--[X]---- + """ + qr = QuantumRegister(2, "qr") + circuit = QuantumCircuit(qr) + circuit.h(0) + circuit.x(1) + circuit.reset(qr) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr) + expected.h(0) + expected.x(1) + + pass_ = RemoveFinalReset() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + +class TestRemoveFinalResetFixedPoint(QiskitTestCase): + """Test RemoveFinalReset in a transpiler, using fixed point.""" + + def test_two_resets(self): + """Remove two final resets + qr0:--[H]-|0>-|0>-- ==> qr0:--[H]-- + """ + qr = QuantumRegister(1, "qr") + circuit = QuantumCircuit(qr) + circuit.h(qr[0]) + circuit.reset(qr[0]) + circuit.reset(qr[0]) + + expected = QuantumCircuit(qr) + expected.h(qr[0]) + + pass_manager = PassManager() + pass_manager.append( + DoWhileController( + [RemoveFinalReset(), DAGFixedPoint()], + do_while=lambda property_set: not property_set["dag_fixed_point"], + ) + ) + after = pass_manager.run(circuit) + + self.assertEqual(expected, after) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/python/transpiler/test_remove_reset_in_zero_state.py b/test/python/transpiler/test_remove_reset_in_zero_state.py index 2c29225dd889..eef3e1c71aa1 100644 --- a/test/python/transpiler/test_remove_reset_in_zero_state.py +++ b/test/python/transpiler/test_remove_reset_in_zero_state.py @@ -23,7 +23,7 @@ class TestRemoveResetInZeroState(QiskitTestCase): - """Test swap-followed-by-measure optimizations.""" + """Test remove-reset-in-zero-state optimizations.""" def test_optimize_single_reset(self): """Remove a single reset