From 69b8066d56bc4388cfcd2531281fd9c992493fa3 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 6 Oct 2021 10:53:44 -0400 Subject: [PATCH] Revert to main preset passmanager logic As was pointed out by @jakelishman in code review. Prior to #6124 the unitary synthesis pass was checking the basis for the presence of unitary (and swap gates if pulse_optimize=True) in the basis set and skipping the gates in the pass directly instead of in the pass manager. This logic was lost when we added the plugin interface in #6124 (probably as part of a rebase as it was a long lived branch). Since we used to have the logic in the pass itself this commit changes the approach to not adjust the preset passmanager usage and just make the pass detect if unitary is in the basis and not synthesize. This simplifies the logic and makes it less error prone. At the same time the test coverage is expanded to ensure we preserve this behavior moving forward (as aer's testing requires it). --- .../passes/synthesis/unitary_synthesis.py | 2 + .../transpiler/preset_passmanagers/level0.py | 21 ++++------ .../transpiler/preset_passmanagers/level1.py | 21 ++++------ .../transpiler/preset_passmanagers/level2.py | 25 +++++------- .../transpiler/preset_passmanagers/level3.py | 21 ++++------ .../transpiler/test_preset_passmanagers.py | 40 +++++++++++++++++++ 6 files changed, 76 insertions(+), 54 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index b4d38d9e3d07..b59b2c5c81fc 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -198,6 +198,8 @@ def run(self, dag: DAGCircuit) -> DAGCircuit: plugin_method._approximation_degree = self._approximation_degree for node in dag.named_nodes(*self._synth_gates): + if self._basis_gates and node.name in self._basis_gates: + continue if self._min_qubits is not None and len(node.qargs) < self._min_qubits: continue if plugin_method.supports_coupling_map: diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index 78ce3dc7b117..070eb8740031 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -157,19 +157,14 @@ def _swap_condition(property_set): elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - if basis_gates is not None and "unitary" not in basis_gates: - _unroll = [ - UnitarySynthesis( - basis_gates, - approximation_degree=approximation_degree, - coupling_map=coupling_map, - backend_props=backend_properties, - method=unitary_synthesis_method, - ) - ] - else: - _unroll = [] - _unroll += [ + _unroll = [ + UnitarySynthesis( + basis_gates, + approximation_degree=approximation_degree, + coupling_map=coupling_map, + backend_props=backend_properties, + method=unitary_synthesis_method, + ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates), ] diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index af2bfa808a4d..6483eebffb03 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -177,21 +177,16 @@ def _swap_condition(property_set): elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - if basis_gates is not None and "unitary" not in basis_gates: + _unroll = [ # Use unitary synthesis for basis aware decomposition of UnitaryGates before # custom unrolling - _unroll = [ - UnitarySynthesis( - basis_gates, - approximation_degree=approximation_degree, - coupling_map=coupling_map, - backend_props=backend_properties, - method=unitary_synthesis_method, - ) - ] - else: - _unroll = [] - _unroll += [ + UnitarySynthesis( + basis_gates, + approximation_degree=approximation_degree, + coupling_map=coupling_map, + method=unitary_synthesis_method, + backend_props=backend_properties, + ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates), ] diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index efb991747100..4daf5b1c1d1a 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -211,21 +211,16 @@ def _swap_condition(property_set): elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - # Use unitary synthesis for basis aware decomposition of UnitaryGates before - # custom unrolling - if basis_gates is not None and "unitary" not in basis_gates: - _unroll = [ - UnitarySynthesis( - basis_gates, - approximation_degree=approximation_degree, - coupling_map=coupling_map, - backend_props=backend_properties, - method=unitary_synthesis_method, - ) - ] - else: - _unroll = [] - _unroll += [ + _unroll = [ + # Use unitary synthesis for basis aware decomposition of UnitaryGates before + # custom unrolling + UnitarySynthesis( + basis_gates, + approximation_degree=approximation_degree, + coupling_map=coupling_map, + backend_props=backend_properties, + method=unitary_synthesis_method, + ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates), ] diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 15b101cefb65..1d0c56264239 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -214,19 +214,14 @@ def _swap_condition(property_set): elif translation_method == "translator": from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel - if basis_gates is not None and "unitary" not in basis_gates: - _unroll = [ - UnitarySynthesis( - basis_gates, - approximation_degree=approximation_degree, - coupling_map=coupling_map, - backend_props=backend_properties, - method=unitary_synthesis_method, - ) - ] - else: - _unroll = [] - _unroll += [ + _unroll = [ + UnitarySynthesis( + basis_gates, + approximation_degree=approximation_degree, + coupling_map=coupling_map, + backend_props=backend_properties, + method=unitary_synthesis_method, + ), UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates), ] diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index 9a8dec5e3678..393be0f832fb 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -110,6 +110,46 @@ def test_unitary_is_preserved_if_in_basis(self, level): result = transpile(qc, basis_gates=["cx", "u", "unitary"], optimization_level=level) self.assertEqual(result, qc) + # Level 3 is skipped because the 2q synthesis optimization pass decomposes + # the unitary + @combine(level=[0, 1, 2], name="level{level}") + def test_unitary_is_preserved_if_basis_is_None(self, level): + """Test that a unitary is not synthesized if basis is None.""" + qc = QuantumCircuit(2) + qc.unitary(random_unitary(4, seed=4242), [0, 1]) + qc.measure_all() + result = transpile(qc, basis_gates=None, optimization_level=level) + self.assertEqual(result, qc) + + # Level 3 is skipped because the 2q synthesis optimization pass decomposes + # the unitary + @combine(level=[0, 1, 2], name="level{level}") + def test_unitary_is_preserved_if_in_basis_synthesis_translation(self, level): + """Test that a unitary is not synthesized if in the basis with synthesis translation.""" + qc = QuantumCircuit(2) + qc.unitary(random_unitary(4, seed=424242), [0, 1]) + qc.measure_all() + result = transpile( + qc, + basis_gates=["cx", "u", "unitary"], + optimization_level=level, + translation_method="synthesis", + ) + self.assertEqual(result, qc) + + # Level 3 is skipped because the 2q synthesis optimization pass decomposes + # the unitary + @combine(level=[0, 1, 2], name="level{level}") + def test_unitary_is_preserved_if_basis_is_None_synthesis_transltion(self, level): + """Test that a unitary is not synthesized if basis is None with synthesis translation.""" + qc = QuantumCircuit(2) + qc.unitary(random_unitary(4, seed=42424242), [0, 1]) + qc.measure_all() + result = transpile( + qc, basis_gates=None, optimization_level=level, translation_method="synthesis" + ) + self.assertEqual(result, qc) + @combine(level=[0, 1, 2, 3], name="level{level}") def test_respect_basis(self, level): """Test that all levels respect basis"""