From be77170a5e3f17436178e537a1145b0b26619799 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 25 Apr 2019 20:35:42 -0400 Subject: [PATCH 01/18] rz_and_z_optimization --- qiskit/transpiler/passes/__init__.py | 1 + .../passes/remove_rz_and_z_before_measure.py | 28 +++++++ .../test_remove_rz_and_z_before_measure.py | 74 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 qiskit/transpiler/passes/remove_rz_and_z_before_measure.py create mode 100644 test/python/transpiler/test_remove_rz_and_z_before_measure.py diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 1f7ba946f43b..8c373bd6d2eb 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -37,5 +37,6 @@ from .mapping.noise_adaptive_layout import NoiseAdaptiveLayout from .mapping.basic_swap import BasicSwap from .mapping.lookahead_swap import LookaheadSwap +from .remove_rz_and_z_before_measure import RemoveRZandZbeforeMeasure from .mapping.stochastic_swap import StochasticSwap from .mapping.legacy_swap import LegacySwap diff --git a/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py b/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py new file mode 100644 index 000000000000..191622157716 --- /dev/null +++ b/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + + +""" +Transpiler pass to remove RZ and Z gate before a measurement +""" + +from qiskit.circuit import Measure +from qiskit.extensions.standard import RZGate, ZGate +from qiskit.transpiler.basepasses import TransformationPass + + +class RemoveRZandZbeforeMeasure(TransformationPass): + """Remove RZ and Z gate before a measurement """ + + def run(self, dag): + """Return a new circuit that has been optimized.""" + measures = dag.op_nodes(Measure) + for measure in measures: + predecessor = next(dag.predecessors(measure)) + if predecessor.type == 'op' and isinstance(predecessor.op, (RZGate, ZGate)): + dag.remove_op_node(predecessor) + return dag diff --git a/test/python/transpiler/test_remove_rz_and_z_before_measure.py b/test/python/transpiler/test_remove_rz_and_z_before_measure.py new file mode 100644 index 000000000000..c32701bf463c --- /dev/null +++ b/test/python/transpiler/test_remove_rz_and_z_before_measure.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +"""Test RemoveRZandZbeforeMeasure pass""" + +import unittest + +from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.transpiler import PassManager, transpile +from qiskit.transpiler.passes import RemoveRZandZbeforeMeasure, DAGFixedPoint +from qiskit.converters import circuit_to_dag +from qiskit.test import QiskitTestCase + + +class TestRemoveRZandZbeforeMeasure(QiskitTestCase): + """ Test remove_rz_and_z_before_measure optimizations. """ + + def test_optimize_1rz_1measure(self): + """ Remove a single RZ + qr0:-RZ--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveRZandZbeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + +class TestRemoveRZandZbeforeMeasureFixedPoint(QiskitTestCase): + """ Test remove_rz_and_z_before_measure optimizations in a transpiler, using fixed point. """ + + def test_optimize_rz_z(self): + """ Remove two swaps that overlap + qr0:--rz-z--m-- qr0:--m-- + | | + cr0:--------.-- cr0:--.-- + """ + qr = QuantumRegister(1, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(99999, qr[0]) + circuit.z(qr[0]) + circuit.measure(qr[0], cr[0]) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_manager = PassManager() + pass_manager.append( + [RemoveRZandZbeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set['dag_fixed_point']) + after = transpile(circuit, pass_manager=pass_manager) + print(after) + self.assertEqual(expected, after) + + +if __name__ == '__main__': + unittest.main() From 29228a44230c2c9e74ada128ade48aa758a872cf Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 25 Apr 2019 21:24:08 -0400 Subject: [PATCH 02/18] bug fixing and more testing --- .../passes/remove_rz_and_z_before_measure.py | 6 +-- .../test_remove_rz_and_z_before_measure.py | 54 +++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py b/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py index 191622157716..b8427ab8d135 100644 --- a/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py +++ b/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py @@ -22,7 +22,7 @@ def run(self, dag): """Return a new circuit that has been optimized.""" measures = dag.op_nodes(Measure) for measure in measures: - predecessor = next(dag.predecessors(measure)) - if predecessor.type == 'op' and isinstance(predecessor.op, (RZGate, ZGate)): - dag.remove_op_node(predecessor) + for predecessor in dag.predecessors(measure): + if predecessor.type == 'op' and isinstance(predecessor.op, (RZGate, ZGate)): + dag.remove_op_node(predecessor) return dag diff --git a/test/python/transpiler/test_remove_rz_and_z_before_measure.py b/test/python/transpiler/test_remove_rz_and_z_before_measure.py index c32701bf463c..5fedc33c5780 100644 --- a/test/python/transpiler/test_remove_rz_and_z_before_measure.py +++ b/test/python/transpiler/test_remove_rz_and_z_before_measure.py @@ -42,19 +42,67 @@ def test_optimize_1rz_1measure(self): self.assertEqual(circuit_to_dag(expected), after) + def test_optimize_1z_1measure(self): + """ Remove a single Z + qr0:--Z--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.z(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveRZandZbeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1rz_1z_1measure(self): + """ Remove a single RZ and leave the other Z + qr0:-RZ--m-- qr0:----m- + | | + qr1:--Z--|-- ==> qr1:--Z-|- + | | + cr0:-----.-- cr0:----.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[0]) + circuit.z(qr[1]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.z(qr[1]) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveRZandZbeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + class TestRemoveRZandZbeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_rz_and_z_before_measure optimizations in a transpiler, using fixed point. """ def test_optimize_rz_z(self): """ Remove two swaps that overlap - qr0:--rz-z--m-- qr0:--m-- + qr0:--RZ-Z--m-- qr0:--m-- | | cr0:--------.-- cr0:--.-- """ qr = QuantumRegister(1, 'qr') cr = ClassicalRegister(1, 'cr') circuit = QuantumCircuit(qr, cr) - circuit.rz(99999, qr[0]) + circuit.rz(0.1, qr[0]) circuit.z(qr[0]) circuit.measure(qr[0], cr[0]) @@ -66,7 +114,7 @@ def test_optimize_rz_z(self): [RemoveRZandZbeforeMeasure(), DAGFixedPoint()], do_while=lambda property_set: not property_set['dag_fixed_point']) after = transpile(circuit, pass_manager=pass_manager) - print(after) + self.assertEqual(expected, after) From 04b14337da7e0a93f218a56800eca580425df3f5 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 25 Apr 2019 21:48:27 -0400 Subject: [PATCH 03/18] changelog --- CHANGELOG.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0ab71a04bcca..076d9ab097d0 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -86,6 +86,8 @@ Added wires instead of named bits. - Added a ``OptimizeSwapBeforeMeasure`` pass that removes the swap gates when they are followed by a measurement instruction, moving the latter to the proper wire. (#1890) +- Added a ``RemoveRZandZbeforeMeasure`` pass that removes the RZ and Z gates when they + are followed by a measurement instruction. (#2208) - Added a ``CommutativeCancellation`` pass that cancels self-inverse gates and combines rotations about the Z axis, leveraging previously-found gate commutation relations. (#2012) - Added a ``Collect2qBlocks`` pass that analyzes the circuit for uninterrupted sequences From cbc43d846649dba92a8f9da57c9c45f0bccc2dc8 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 08:30:53 -0400 Subject: [PATCH 04/18] lint --- test/python/transpiler/test_remove_rz_and_z_before_measure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/python/transpiler/test_remove_rz_and_z_before_measure.py b/test/python/transpiler/test_remove_rz_and_z_before_measure.py index 5fedc33c5780..3e1a99c272eb 100644 --- a/test/python/transpiler/test_remove_rz_and_z_before_measure.py +++ b/test/python/transpiler/test_remove_rz_and_z_before_measure.py @@ -90,6 +90,7 @@ def test_optimize_1rz_1z_1measure(self): self.assertEqual(circuit_to_dag(expected), after) + class TestRemoveRZandZbeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_rz_and_z_before_measure optimizations in a transpiler, using fixed point. """ From ed7742d14d57b82bfc16d5484519eab2333da213 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 08:54:46 -0400 Subject: [PATCH 05/18] RemoveRZandZbeforeMeasure -> RemoveDiagonalGatesBeforeMeasure --- CHANGELOG.rst | 2 +- qiskit/transpiler/passes/__init__.py | 2 +- ...remove_diagonal_gates_before_measurere.py} | 12 +- ...st_remove_diagonal_gates_before_measure.py | 239 ++++++++++++++++++ .../test_remove_rz_and_z_before_measure.py | 123 --------- 5 files changed, 248 insertions(+), 130 deletions(-) rename qiskit/transpiler/passes/{remove_rz_and_z_before_measure.py => remove_diagonal_gates_before_measurere.py} (61%) create mode 100644 test/python/transpiler/test_remove_diagonal_gates_before_measure.py delete mode 100644 test/python/transpiler/test_remove_rz_and_z_before_measure.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3eeeab1d9b0e..c5575da51c36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -85,7 +85,7 @@ Added wires instead of named bits. - Added a ``OptimizeSwapBeforeMeasure`` pass that removes the swap gates when they are followed by a measurement instruction, moving the latter to the proper wire. (#1890) -- Added a ``RemoveRZandZbeforeMeasure`` pass that removes the RZ and Z gates when they +- Added a ``RemoveDiagonalGatesBeforeMeasure`` pass that removes the RZ and Z gates when they are followed by a measurement instruction. (#2208) - Added a ``CommutativeCancellation`` pass that cancels self-inverse gates and combines rotations about the Z axis, leveraging previously-found gate commutation relations. (#2012) diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index 8c373bd6d2eb..ec50a1bb67d1 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -37,6 +37,6 @@ from .mapping.noise_adaptive_layout import NoiseAdaptiveLayout from .mapping.basic_swap import BasicSwap from .mapping.lookahead_swap import LookaheadSwap -from .remove_rz_and_z_before_measure import RemoveRZandZbeforeMeasure +from .remove_diagonal_gates_before_measurere import RemoveDiagonalGatesBeforeMeasure from .mapping.stochastic_swap import StochasticSwap from .mapping.legacy_swap import LegacySwap diff --git a/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py similarity index 61% rename from qiskit/transpiler/passes/remove_rz_and_z_before_measure.py rename to qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py index b8427ab8d135..f4485c936846 100644 --- a/qiskit/transpiler/passes/remove_rz_and_z_before_measure.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py @@ -7,22 +7,24 @@ """ -Transpiler pass to remove RZ and Z gate before a measurement +Transpiler pass to remove diagonal gates (like RZ, T, Z, etc) before a measurement """ from qiskit.circuit import Measure -from qiskit.extensions.standard import RZGate, ZGate +from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate from qiskit.transpiler.basepasses import TransformationPass -class RemoveRZandZbeforeMeasure(TransformationPass): - """Remove RZ and Z gate before a measurement """ +class RemoveDiagonalGatesBeforeMeasure(TransformationPass): + """Remove diagonal gates (like RZ, T, Z, etc) before a measurement""" def run(self, dag): """Return a new circuit that has been optimized.""" + DiagonalGates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) + measures = dag.op_nodes(Measure) for measure in measures: for predecessor in dag.predecessors(measure): - if predecessor.type == 'op' and isinstance(predecessor.op, (RZGate, ZGate)): + if predecessor.type == 'op' and isinstance(predecessor.op, DiagonalGates): dag.remove_op_node(predecessor) return dag diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py new file mode 100644 index 000000000000..b458ed5e3787 --- /dev/null +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +"""Test RemoveDiagonalGatesBeforeMeasure pass""" + +import unittest + +from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister +from qiskit.transpiler import PassManager, transpile +from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure, DAGFixedPoint +from qiskit.converters import circuit_to_dag +from qiskit.test import QiskitTestCase + + +class TesRemoveDiagonalGatesBeforeMeasure(QiskitTestCase): + """ Test remove_diagonal_gates_before_measure optimizations. """ + + def test_optimize_1rz_1measure(self): + """ Remove a single RZGate + qr0:-RZ--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1z_1measure(self): + """ Remove a single ZGate + qr0:--Z--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.z(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1t_1measure(self): + """ Remove a single TGate, SGate, TdgGate, SdgGate, U1Gate + qr0:--T--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.t(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1s_1measure(self): + """ Remove a single SGate + qr0:--T--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.t(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1tdg_1measure(self): + """ Remove a single TdgGate, SdgGate, U1Gate + qr0:--T--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.tdg(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1sdg_1measure(self): + """ Remove a single SdgGate + qr0:--T--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.sdg(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1u1_1measure(self): + """ Remove a single U1Gate + qr0:--T--m-- qr0:--m- + | | + qr1:-----|-- ==> qr1:--|- + | | + cr0:-----.-- cr0:--.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.u1(0.1, qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1rz_1z_1measure(self): + """ Remove a single RZ and leave the other Z + qr0:-RZ--m-- qr0:----m- + | | + qr1:--Z--|-- ==> qr1:--Z-|- + | | + cr0:-----.-- cr0:----.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[0]) + circuit.z(qr[1]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.z(qr[1]) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + +class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): + """ Test remove_diagonal_gates_before_measure optimizations in + a transpiler, using fixed point. """ + + def test_optimize_rz_z(self): + """ Remove two swaps that overlap + qr0:--RZ-Z--m-- qr0:--m-- + | | + cr0:--------.-- cr0:--.-- + """ + qr = QuantumRegister(1, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[0]) + circuit.z(qr[0]) + circuit.measure(qr[0], cr[0]) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + + pass_manager = PassManager() + pass_manager.append( + [RemoveDiagonalGatesBeforeMeasure(), DAGFixedPoint()], + do_while=lambda property_set: not property_set['dag_fixed_point']) + after = transpile(circuit, pass_manager=pass_manager) + + self.assertEqual(expected, after) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/python/transpiler/test_remove_rz_and_z_before_measure.py b/test/python/transpiler/test_remove_rz_and_z_before_measure.py deleted file mode 100644 index 3e1a99c272eb..000000000000 --- a/test/python/transpiler/test_remove_rz_and_z_before_measure.py +++ /dev/null @@ -1,123 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2019, IBM. -# -# This source code is licensed under the Apache License, Version 2.0 found in -# the LICENSE.txt file in the root directory of this source tree. - -"""Test RemoveRZandZbeforeMeasure pass""" - -import unittest - -from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister -from qiskit.transpiler import PassManager, transpile -from qiskit.transpiler.passes import RemoveRZandZbeforeMeasure, DAGFixedPoint -from qiskit.converters import circuit_to_dag -from qiskit.test import QiskitTestCase - - -class TestRemoveRZandZbeforeMeasure(QiskitTestCase): - """ Test remove_rz_and_z_before_measure optimizations. """ - - def test_optimize_1rz_1measure(self): - """ Remove a single RZ - qr0:-RZ--m-- qr0:--m- - | | - qr1:-----|-- ==> qr1:--|- - | | - cr0:-----.-- cr0:--.- - """ - qr = QuantumRegister(2, 'qr') - cr = ClassicalRegister(1, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.rz(0.1, qr[0]) - circuit.measure(qr[0], cr[0]) - dag = circuit_to_dag(circuit) - - expected = QuantumCircuit(qr, cr) - expected.measure(qr[0], cr[0]) - - pass_ = RemoveRZandZbeforeMeasure() - after = pass_.run(dag) - - self.assertEqual(circuit_to_dag(expected), after) - - def test_optimize_1z_1measure(self): - """ Remove a single Z - qr0:--Z--m-- qr0:--m- - | | - qr1:-----|-- ==> qr1:--|- - | | - cr0:-----.-- cr0:--.- - """ - qr = QuantumRegister(2, 'qr') - cr = ClassicalRegister(1, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.z(qr[0]) - circuit.measure(qr[0], cr[0]) - dag = circuit_to_dag(circuit) - - expected = QuantumCircuit(qr, cr) - expected.measure(qr[0], cr[0]) - - pass_ = RemoveRZandZbeforeMeasure() - after = pass_.run(dag) - - self.assertEqual(circuit_to_dag(expected), after) - - def test_optimize_1rz_1z_1measure(self): - """ Remove a single RZ and leave the other Z - qr0:-RZ--m-- qr0:----m- - | | - qr1:--Z--|-- ==> qr1:--Z-|- - | | - cr0:-----.-- cr0:----.- - """ - qr = QuantumRegister(2, 'qr') - cr = ClassicalRegister(1, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.rz(0.1, qr[0]) - circuit.z(qr[1]) - circuit.measure(qr[0], cr[0]) - dag = circuit_to_dag(circuit) - - expected = QuantumCircuit(qr, cr) - expected.z(qr[1]) - expected.measure(qr[0], cr[0]) - - pass_ = RemoveRZandZbeforeMeasure() - after = pass_.run(dag) - - self.assertEqual(circuit_to_dag(expected), after) - - -class TestRemoveRZandZbeforeMeasureFixedPoint(QiskitTestCase): - """ Test remove_rz_and_z_before_measure optimizations in a transpiler, using fixed point. """ - - def test_optimize_rz_z(self): - """ Remove two swaps that overlap - qr0:--RZ-Z--m-- qr0:--m-- - | | - cr0:--------.-- cr0:--.-- - """ - qr = QuantumRegister(1, 'qr') - cr = ClassicalRegister(1, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.rz(0.1, qr[0]) - circuit.z(qr[0]) - circuit.measure(qr[0], cr[0]) - - expected = QuantumCircuit(qr, cr) - expected.measure(qr[0], cr[0]) - - pass_manager = PassManager() - pass_manager.append( - [RemoveRZandZbeforeMeasure(), DAGFixedPoint()], - do_while=lambda property_set: not property_set['dag_fixed_point']) - after = transpile(circuit, pass_manager=pass_manager) - - self.assertEqual(expected, after) - - -if __name__ == '__main__': - unittest.main() From 07499bd238742abd3ba8407ca47660b5c2610f94 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 08:57:00 -0400 Subject: [PATCH 06/18] changelog --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c5575da51c36..eb3158829ffe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -85,7 +85,7 @@ Added wires instead of named bits. - Added a ``OptimizeSwapBeforeMeasure`` pass that removes the swap gates when they are followed by a measurement instruction, moving the latter to the proper wire. (#1890) -- Added a ``RemoveDiagonalGatesBeforeMeasure`` pass that removes the RZ and Z gates when they +- Added a ``RemoveDiagonalGatesBeforeMeasure`` pass that removes the diagonal gates when they are followed by a measurement instruction. (#2208) - Added a ``CommutativeCancellation`` pass that cancels self-inverse gates and combines rotations about the Z axis, leveraging previously-found gate commutation relations. (#2012) From d18a6fdc85c093e417f9c108c49481a7decf2d0c Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 08:59:03 -0400 Subject: [PATCH 07/18] invalid-name --- .../passes/remove_diagonal_gates_before_measurere.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py index f4485c936846..344e4fce6421 100644 --- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py @@ -20,11 +20,11 @@ class RemoveDiagonalGatesBeforeMeasure(TransformationPass): def run(self, dag): """Return a new circuit that has been optimized.""" - DiagonalGates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) + diagonal_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) measures = dag.op_nodes(Measure) for measure in measures: for predecessor in dag.predecessors(measure): - if predecessor.type == 'op' and isinstance(predecessor.op, DiagonalGates): + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_gates): dag.remove_op_node(predecessor) return dag From cc7683f96c710147b11b7fe44fc3b0e830b8ef90 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 09:01:20 -0400 Subject: [PATCH 08/18] test --- ...st_remove_diagonal_gates_before_measure.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index b458ed5e3787..b55a15bbb922 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -90,7 +90,7 @@ def test_optimize_1t_1measure(self): def test_optimize_1s_1measure(self): """ Remove a single SGate - qr0:--T--m-- qr0:--m- + qr0:--S--m-- qr0:--m- | | qr1:-----|-- ==> qr1:--|- | | @@ -99,7 +99,7 @@ def test_optimize_1s_1measure(self): qr = QuantumRegister(2, 'qr') cr = ClassicalRegister(1, 'cr') circuit = QuantumCircuit(qr, cr) - circuit.t(qr[0]) + circuit.s(qr[0]) circuit.measure(qr[0], cr[0]) dag = circuit_to_dag(circuit) @@ -112,8 +112,8 @@ def test_optimize_1s_1measure(self): self.assertEqual(circuit_to_dag(expected), after) def test_optimize_1tdg_1measure(self): - """ Remove a single TdgGate, SdgGate, U1Gate - qr0:--T--m-- qr0:--m- + """ Remove a single TdgGate + qr0:-Tdg-m-- qr0:--m- | | qr1:-----|-- ==> qr1:--|- | | @@ -136,11 +136,11 @@ def test_optimize_1tdg_1measure(self): def test_optimize_1sdg_1measure(self): """ Remove a single SdgGate - qr0:--T--m-- qr0:--m- - | | - qr1:-----|-- ==> qr1:--|- - | | - cr0:-----.-- cr0:--.- + qr0:-Sdg--m-- qr0:--m- + | | + qr1:------|-- ==> qr1:--|- + | | + cr0:------.-- cr0:--.- """ qr = QuantumRegister(2, 'qr') cr = ClassicalRegister(1, 'cr') @@ -159,7 +159,7 @@ def test_optimize_1sdg_1measure(self): def test_optimize_1u1_1measure(self): """ Remove a single U1Gate - qr0:--T--m-- qr0:--m- + qr0:--U1-m-- qr0:--m- | | qr1:-----|-- ==> qr1:--|- | | From 20e6271cd4ef992552afe40c52edb504bfe5645c Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 11:17:59 -0400 Subject: [PATCH 09/18] diagonal_control_gates --- .../remove_diagonal_gates_before_measurere.py | 25 +++++++-- ...st_remove_diagonal_gates_before_measure.py | 55 +++++++++++++++++++ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py index 344e4fce6421..d1d3405b85d5 100644 --- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py @@ -7,24 +7,37 @@ """ -Transpiler pass to remove diagonal gates (like RZ, T, Z, etc) before a measurement +Transpiler pass to remove diagonal gates (like RZ, T, Z, etc) before +a measurement. Including diagonal control gates. """ from qiskit.circuit import Measure -from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate +from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate, CzGate from qiskit.transpiler.basepasses import TransformationPass class RemoveDiagonalGatesBeforeMeasure(TransformationPass): - """Remove diagonal gates (like RZ, T, Z, etc) before a measurement""" + """Remove diagonal gates (like RZ, T, Z, etc) before a measurement. + Including diagonal control gates.""" def run(self, dag): """Return a new circuit that has been optimized.""" diagonal_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) + diagonal_control_gates = (CzGate) - measures = dag.op_nodes(Measure) - for measure in measures: + nodes_to_remove = set() + for measure in dag.op_nodes(Measure): for predecessor in dag.predecessors(measure): + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_gates): - dag.remove_op_node(predecessor) + nodes_to_remove.add(predecessor) + + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_control_gates): + successors = dag.successors(predecessor) + if all([s.type == 'op' and isinstance(s.op, Measure) for s in successors]): + nodes_to_remove.add(predecessor) + + for node_to_remove in nodes_to_remove: + dag.remove_op_node(node_to_remove) + return dag diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index b55a15bbb922..f54811d6424d 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -206,6 +206,61 @@ def test_optimize_1rz_1z_1measure(self): self.assertEqual(circuit_to_dag(expected), after) +class TesRemoveDiagonalControlGatesBeforeMeasure(QiskitTestCase): + """ Test remove diagonal control gates before measure. """ + + def test_optimize_1cz_1measure(self): + """ Do not remove a CzGate because measure happens on only one of the wires + Compare with test_optimize_1cz_2measure. + + qr0:--Z--m--- + | | + qr1:--.--|--- + | + cr0:-----.--- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.cz(qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.cz(qr[0], qr[1]) + expected.measure(qr[0], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1cz_2measure(self): + """ Remove a single CzGate + qr0:--Z--m--- qr0:--m--- + | | | + qr1:--.--|-m- ==> qr1:--|-m- + | | | | + cr0:-----.-.- cr0:--.-.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.cz(qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + circuit.measure(qr[1], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + expected.measure(qr[1], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_diagonal_gates_before_measure optimizations in a transpiler, using fixed point. """ From 8e214af602262ecde4347ea3c30691ff16c4e343 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 12:23:31 -0400 Subject: [PATCH 10/18] CrzGate, Cu1Gate, RZZGate --- .../remove_diagonal_gates_before_measurere.py | 5 +- ...st_remove_diagonal_gates_before_measure.py | 74 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py index d1d3405b85d5..e08a4538f700 100644 --- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py @@ -12,7 +12,8 @@ """ from qiskit.circuit import Measure -from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate, CzGate +from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate,\ + CzGate, CrzGate, Cu1Gate, RZZGate from qiskit.transpiler.basepasses import TransformationPass @@ -23,7 +24,7 @@ class RemoveDiagonalGatesBeforeMeasure(TransformationPass): def run(self, dag): """Return a new circuit that has been optimized.""" diagonal_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) - diagonal_control_gates = (CzGate) + diagonal_control_gates = (CzGate, CrzGate, Cu1Gate, RZZGate) nodes_to_remove = set() for measure in dag.op_nodes(Measure): diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index f54811d6424d..9a5fdb3c3335 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -260,6 +260,80 @@ def test_optimize_1cz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) + def test_optimize_1crz_2measure(self): + """ Remove a single CrzGate + qr0:-RZ--m--- qr0:--m--- + | | | + qr1:--.--|-m- ==> qr1:--|-m- + | | | | + cr0:-----.-.- cr0:--.-.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.crz(0.1, qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + circuit.measure(qr[1], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + expected.measure(qr[1], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1cu1_2measure(self): + """ Remove a single Cu1Gate + qr0:-CU1-m--- qr0:--m--- + | | | + qr1:--.--|-m- ==> qr1:--|-m- + | | | | + cr0:-----.-.- cr0:--.-.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.cu1(0.1, qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + circuit.measure(qr[1], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + expected.measure(qr[1], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) + + def test_optimize_1rzz_2measure(self): + """ Remove a single RZZGate + qr0:-RZZ-m--- qr0:--m--- + | | | + qr1:--.--|-m- ==> qr1:--|-m- + | | | | + cr0:-----.-.- cr0:--.-.- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rzz(0.1, qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + circuit.measure(qr[1], cr[0]) + dag = circuit_to_dag(circuit) + + expected = QuantumCircuit(qr, cr) + expected.measure(qr[0], cr[0]) + expected.measure(qr[1], cr[0]) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(circuit_to_dag(expected), after) class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_diagonal_gates_before_measure optimizations in From c1947812faed985d580cfdf76227d4683e161817 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 12:37:35 -0400 Subject: [PATCH 11/18] lint --- .../transpiler/test_remove_diagonal_gates_before_measure.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index 9a5fdb3c3335..e04fd18c24d0 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -335,6 +335,7 @@ def test_optimize_1rzz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) + class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_diagonal_gates_before_measure optimizations in a transpiler, using fixed point. """ From f1659fe61f549baaeb2d5709ee4ba8abfbfe4e85 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 21:44:32 -0400 Subject: [PATCH 12/18] from qiskit.compiler import transpile --- .../transpiler/test_remove_diagonal_gates_before_measure.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index e04fd18c24d0..80b6301f09f0 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -10,7 +10,8 @@ import unittest from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister -from qiskit.transpiler import PassManager, transpile +from qiskit.transpiler import PassManager +from qiskit.compiler import transpile from qiskit.transpiler.passes import RemoveDiagonalGatesBeforeMeasure, DAGFixedPoint from qiskit.converters import circuit_to_dag from qiskit.test import QiskitTestCase From e9fcd4923fdaef9a5f0c656dae9f9287312cf5d6 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 21:51:13 -0400 Subject: [PATCH 13/18] remove_diagonal_gates_before_measurere --- qiskit/transpiler/passes/__init__.py | 2 +- ...ore_measurere.py => remove_diagonal_gates_before_measure.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename qiskit/transpiler/passes/{remove_diagonal_gates_before_measurere.py => remove_diagonal_gates_before_measure.py} (100%) diff --git a/qiskit/transpiler/passes/__init__.py b/qiskit/transpiler/passes/__init__.py index ec50a1bb67d1..aeaa7c594cd1 100644 --- a/qiskit/transpiler/passes/__init__.py +++ b/qiskit/transpiler/passes/__init__.py @@ -37,6 +37,6 @@ from .mapping.noise_adaptive_layout import NoiseAdaptiveLayout from .mapping.basic_swap import BasicSwap from .mapping.lookahead_swap import LookaheadSwap -from .remove_diagonal_gates_before_measurere import RemoveDiagonalGatesBeforeMeasure +from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure from .mapping.stochastic_swap import StochasticSwap from .mapping.legacy_swap import LegacySwap diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py similarity index 100% rename from qiskit/transpiler/passes/remove_diagonal_gates_before_measurere.py rename to qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py From e63d4b2d73a0b80e21579e1c33385b7bfc5d6079 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 21:55:19 -0400 Subject: [PATCH 14/18] diagonal_control_gates -> diagonal_2q_gates --- .../passes/remove_diagonal_gates_before_measure.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py index e08a4538f700..d4a54122a370 100644 --- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py @@ -8,7 +8,7 @@ """ Transpiler pass to remove diagonal gates (like RZ, T, Z, etc) before -a measurement. Including diagonal control gates. +a measurement. Including diagonal 2Q gates. """ from qiskit.circuit import Measure @@ -19,21 +19,21 @@ class RemoveDiagonalGatesBeforeMeasure(TransformationPass): """Remove diagonal gates (like RZ, T, Z, etc) before a measurement. - Including diagonal control gates.""" + Including diagonal 2Q gates.""" def run(self, dag): """Return a new circuit that has been optimized.""" - diagonal_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) - diagonal_control_gates = (CzGate, CrzGate, Cu1Gate, RZZGate) + diagonal_1q_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate) + diagonal_2q_gates = (CzGate, CrzGate, Cu1Gate, RZZGate) nodes_to_remove = set() for measure in dag.op_nodes(Measure): for predecessor in dag.predecessors(measure): - if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_gates): + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_1q_gates): nodes_to_remove.add(predecessor) - if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_control_gates): + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_2q_gates): successors = dag.successors(predecessor) if all([s.type == 'op' and isinstance(s.op, Measure) for s in successors]): nodes_to_remove.add(predecessor) From 019ca0bed0aeb0a4c3aab26f2622abcf58a75a8c Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 22:21:18 -0400 Subject: [PATCH 15/18] quantum_predecessors --- qiskit/dagcircuit/dagcircuit.py | 11 +++++++++++ test/python/test_dagcircuit.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 10cd8834f60f..300d71bd68fc 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -1089,6 +1089,17 @@ def predecessors(self, node): return self._multi_graph.predecessors(node) + def quantum_predecessors(self, node): + """Returns list of the predecessors of a node that are + connected by a quantum edge as DAGNodes.""" + + predecessors = [] + for predecessor in self.predecessors(node): + if isinstance(self._multi_graph.get_edge_data(predecessor, node, key=0)['wire'][0], + QuantumRegister): + predecessors.append(predecessor) + return predecessors + def ancestors(self, node): """Returns set of the ancestors of a node as DAGNodes.""" if isinstance(node, int): diff --git a/test/python/test_dagcircuit.py b/test/python/test_dagcircuit.py index c856c354f159..8b67959dbe78 100644 --- a/test/python/test_dagcircuit.py +++ b/test/python/test_dagcircuit.py @@ -168,6 +168,24 @@ def test_quantum_successors(self): self.assertEqual(successor_cnot[0].type, 'out') self.assertIsInstance(successor_cnot[1].op, Reset) + def test_quantum_predecessors(self): + """The method dag.quantum_predecessors() returns predecessors connected by quantum edges""" + self.dag.apply_operation_back(Reset(), [self.qubit0], []) + self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], []) + self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], []) + + predecessor_measure = self.dag.quantum_predecessors( + self.dag.named_nodes('measure').pop()) + self.assertEqual(len(predecessor_measure), 1) + cnot_node = predecessor_measure[0] + + self.assertIsInstance(cnot_node.op, CnotGate) + + predecessor_cnot = self.dag.quantum_predecessors(cnot_node) + self.assertEqual(len(predecessor_cnot), 2) + self.assertEqual(predecessor_cnot[1].type, 'in') + self.assertIsInstance(predecessor_cnot[0].op, Reset) + def test_get_gates_nodes(self): """The method dag.gate_nodes() returns all gate nodes""" self.dag.apply_operation_back(HGate(), [self.qubit0], []) From 11a0a74377cacae32046c2191ffdfb6386ba96e4 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 22:31:29 -0400 Subject: [PATCH 16/18] use quantum_predecessors --- .../remove_diagonal_gates_before_measure.py | 14 ++-- ...st_remove_diagonal_gates_before_measure.py | 80 +++++++++++++------ 2 files changed, 61 insertions(+), 33 deletions(-) diff --git a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py index d4a54122a370..d432fb942cf7 100644 --- a/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py +++ b/qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py @@ -28,15 +28,15 @@ def run(self, dag): nodes_to_remove = set() for measure in dag.op_nodes(Measure): - for predecessor in dag.predecessors(measure): + predecessor = dag.quantum_predecessors(measure)[0] - if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_1q_gates): - nodes_to_remove.add(predecessor) + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_1q_gates): + nodes_to_remove.add(predecessor) - if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_2q_gates): - successors = dag.successors(predecessor) - if all([s.type == 'op' and isinstance(s.op, Measure) for s in successors]): - nodes_to_remove.add(predecessor) + if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_2q_gates): + successors = dag.quantum_successors(predecessor) + if all([s.type == 'op' and isinstance(s.op, Measure) for s in successors]): + nodes_to_remove.add(predecessor) for node_to_remove in nodes_to_remove: dag.remove_op_node(node_to_remove) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index 80b6301f09f0..9313da506c4b 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -8,6 +8,7 @@ """Test RemoveDiagonalGatesBeforeMeasure pass""" import unittest +from copy import deepcopy from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.transpiler import PassManager @@ -210,32 +211,6 @@ def test_optimize_1rz_1z_1measure(self): class TesRemoveDiagonalControlGatesBeforeMeasure(QiskitTestCase): """ Test remove diagonal control gates before measure. """ - def test_optimize_1cz_1measure(self): - """ Do not remove a CzGate because measure happens on only one of the wires - Compare with test_optimize_1cz_2measure. - - qr0:--Z--m--- - | | - qr1:--.--|--- - | - cr0:-----.--- - """ - qr = QuantumRegister(2, 'qr') - cr = ClassicalRegister(1, 'cr') - circuit = QuantumCircuit(qr, cr) - circuit.cz(qr[0], qr[1]) - circuit.measure(qr[0], cr[0]) - dag = circuit_to_dag(circuit) - - expected = QuantumCircuit(qr, cr) - expected.cz(qr[0], qr[1]) - expected.measure(qr[0], cr[0]) - - pass_ = RemoveDiagonalGatesBeforeMeasure() - after = pass_.run(dag) - - self.assertEqual(circuit_to_dag(expected), after) - def test_optimize_1cz_2measure(self): """ Remove a single CzGate qr0:--Z--m--- qr0:--m--- @@ -336,6 +311,59 @@ def test_optimize_1rzz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) +class TestRemoveDiagonalGatesBeforeMeasureOveroptimizations(QiskitTestCase): + """ Test situations where remove_diagonal_gates_before_measure should not optimize """ + + def test_optimize_1cz_1measure(self): + """ Do not remove a CzGate because measure happens on only one of the wires + Compare with test_optimize_1cz_2measure. + + qr0:--Z--m--- + | | + qr1:--.--|--- + | + cr0:-----.--- + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.cz(qr[0], qr[1]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = deepcopy(dag) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(expected, after) + + def test_do_not_optimize_with_conditional(self): + """ Diagonal gates with conditionals on a measurement target. + See https://github.com/Qiskit/qiskit-terra/pull/2208#issuecomment-487238819 + ░ ┌───┐┌─┐ + qr_0: |0>────────────░─┤ H ├┤M├ + ┌─────────┐ ░ └───┘└╥┘ + qr_1: |0>┤ Rz(0.1) ├─░───────╫─ + └─┬──┴──┬─┘ ░ ║ + cr_0: 0 ══╡ = 1 ╞═══════════╩═ + └─────┘ + """ + qr = QuantumRegister(2, 'qr') + cr = ClassicalRegister(1, 'cr') + circuit = QuantumCircuit(qr, cr) + circuit.rz(0.1, qr[1]).c_if(cr, 1) + circuit.barrier() + circuit.h(qr[0]) + circuit.measure(qr[0], cr[0]) + dag = circuit_to_dag(circuit) + + expected = deepcopy(dag) + + pass_ = RemoveDiagonalGatesBeforeMeasure() + after = pass_.run(dag) + + self.assertEqual(expected, after) class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_diagonal_gates_before_measure optimizations in From 744f5e3013b108438c3063257c256daf0a644c85 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 22:36:53 -0400 Subject: [PATCH 17/18] test_optimize_1rzz_2measure docstring --- .../test_remove_diagonal_gates_before_measure.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index 9313da506c4b..c7feaae6e021 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -288,11 +288,11 @@ def test_optimize_1cu1_2measure(self): def test_optimize_1rzz_2measure(self): """ Remove a single RZZGate - qr0:-RZZ-m--- qr0:--m--- - | | | - qr1:--.--|-m- ==> qr1:--|-m- - | | | | - cr0:-----.-.- cr0:--.-.- + qr0:--.----m--- qr0:--m--- + |zz | | + qr1:--.----|-m- ==> qr1:--|-m- + | | | | + cr0:-------.-.- cr0:--.-.- """ qr = QuantumRegister(2, 'qr') cr = ClassicalRegister(1, 'cr') From 1a808c31c910be4733ce463d2e8556190b19940c Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 26 Apr 2019 22:46:33 -0400 Subject: [PATCH 18/18] lint --- .../transpiler/test_remove_diagonal_gates_before_measure.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py index c7feaae6e021..99d433a267a9 100644 --- a/test/python/transpiler/test_remove_diagonal_gates_before_measure.py +++ b/test/python/transpiler/test_remove_diagonal_gates_before_measure.py @@ -311,6 +311,7 @@ def test_optimize_1rzz_2measure(self): self.assertEqual(circuit_to_dag(expected), after) + class TestRemoveDiagonalGatesBeforeMeasureOveroptimizations(QiskitTestCase): """ Test situations where remove_diagonal_gates_before_measure should not optimize """ @@ -365,6 +366,7 @@ def test_do_not_optimize_with_conditional(self): self.assertEqual(expected, after) + class TestRemoveDiagonalGatesBeforeMeasureFixedPoint(QiskitTestCase): """ Test remove_diagonal_gates_before_measure optimizations in a transpiler, using fixed point. """