From 43e636fcbd202901e5cf1c1b4b341cc8b4cc2907 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 28 Apr 2022 17:02:28 -0400 Subject: [PATCH 1/4] Support target and basis gates in Unroll3qOrMore transpiler pass This commit adds two new constructor arguments to the Unroll3qOrMore transpiler pass, target and basis_gates, which are used to specify a backend target and basis gate list respectively. If these are specified the pass will not decompose any multiqubit operations present in the circuit if they are in the target or basis_gates list. Fixes #5518 Related to #7812 Part of #7113 --- .../passes/basis/unroll_3q_or_more.py | 30 ++++++++++++++ .../transpiler/preset_passmanagers/level0.py | 4 +- .../transpiler/preset_passmanagers/level1.py | 4 +- .../transpiler/preset_passmanagers/level2.py | 4 +- .../transpiler/preset_passmanagers/level3.py | 4 +- .../unroll3q-target-bf57cc4365808862.yaml | 10 +++++ .../transpiler/test_unroll_3q_or_more.py | 39 +++++++++++++++++++ 7 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml diff --git a/qiskit/transpiler/passes/basis/unroll_3q_or_more.py b/qiskit/transpiler/passes/basis/unroll_3q_or_more.py index a1656d554249..4f0ec9069f86 100644 --- a/qiskit/transpiler/passes/basis/unroll_3q_or_more.py +++ b/qiskit/transpiler/passes/basis/unroll_3q_or_more.py @@ -20,6 +20,27 @@ class Unroll3qOrMore(TransformationPass): """Recursively expands 3q+ gates until the circuit only contains 2q or 1q gates.""" + def __init__(self, target=None, basis_gates=None): + """Initialize the Unroll3qOrMore pass + + Args: + target (Target): The target object reprsenting the compilation + target. If specified any multiqubit instructions in the + circuit when the pass is run that are supported by the target + device will be left in place. If both this and ``basis_gates`` + are specified only the target will be checked. + basis_gates (list): A list of basis gate names that the target + device supports. If specified any gate names in the circuit + which are present in this list will not be unrolled. If both + this and ``target`` are specified only the target will be used + for checking which gates are supported. + """ + super().__init__() + self.target = target + self.basis_gates = None + if basis_gates is not None: + self.basis_gates = set(basis_gates) + def run(self, dag): """Run the Unroll3qOrMore pass on `dag`. @@ -33,6 +54,15 @@ def run(self, dag): for node in dag.multi_qubit_ops(): if dag.has_calibration_for(node): continue + if self.target is not None: + # Treat target instructions as global since this pass can be run + # prior to layout and routing we don't have phsyical qubits from + # the circuit yet + if node.name in self.target: + continue + elif self.basis_gates is not None and node.name in self.basis_gates: + continue + # TODO: allow choosing other possible decompositions rule = node.op.definition.data if not rule: diff --git a/qiskit/transpiler/preset_passmanagers/level0.py b/qiskit/transpiler/preset_passmanagers/level0.py index a0596daea156..6d3934575717 100644 --- a/qiskit/transpiler/preset_passmanagers/level0.py +++ b/qiskit/transpiler/preset_passmanagers/level0.py @@ -108,7 +108,7 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: plugin_config=unitary_synthesis_plugin_config, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), ] # 2. Choose an initial layout if not set by user (default: trivial layout) @@ -190,7 +190,7 @@ def _swap_condition(property_set): plugin_config=unitary_synthesis_plugin_config, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), Collect2qBlocks(), Collect1qRuns(), ConsolidateBlocks(basis_gates=basis_gates, target=target), diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index d7bcd8a9940c..76d1de8db6ea 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -173,7 +173,7 @@ def _vf2_match_not_found(property_set): plugin_config=unitary_synthesis_plugin_config, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), ] # 3. Use a better layout on densely connected qubits, if circuit needs swaps @@ -253,7 +253,7 @@ def _swap_condition(property_set): min_qubits=3, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index bfed693636aa..80ff4374e32b 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -122,7 +122,7 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: plugin_config=unitary_synthesis_plugin_config, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), ] # 2. Search for a perfect layout, or choose a dense layout, if no layout given @@ -238,7 +238,7 @@ def _swap_condition(property_set): min_qubits=3, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py index 3827d7aab151..b896339cc7c0 100644 --- a/qiskit/transpiler/preset_passmanagers/level3.py +++ b/qiskit/transpiler/preset_passmanagers/level3.py @@ -125,7 +125,7 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager: min_qubits=3, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), ] # 2. Layout on good qubits if calibration info available, otherwise on dense links @@ -236,7 +236,7 @@ def _swap_condition(property_set): min_qubits=3, target=target, ), - Unroll3qOrMore(), + Unroll3qOrMore(target=target, basis_gates=basis_gates), Collect2qBlocks(), ConsolidateBlocks(basis_gates=basis_gates, target=target), UnitarySynthesis( diff --git a/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml new file mode 100644 index 000000000000..ecc8a655f3ef --- /dev/null +++ b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The constructor for the :class:`~.Unroll3qOrMore` transpiler pass has + two new optional keyword arguments, ``target`` and ``basis`` gates. These + options enable you to specify the :class:`~.Target` or supported basis + gates respectively to describe the target backend. If any of the operations + in the circuit are in the ``target`` or ``basis_gates`` those will not + be unrolled by the pass as the target device has native support for the + operation. diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py index 571ffb6d58e8..c7d084246073 100644 --- a/test/python/transpiler/test_unroll_3q_or_more.py +++ b/test/python/transpiler/test_unroll_3q_or_more.py @@ -12,13 +12,16 @@ """Test the Unroll3qOrMore pass""" import numpy as np + from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit +from qiskit.circuit.library import CCXGate, RCCXGate from qiskit.transpiler.passes import Unroll3qOrMore from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.quantum_info.operators import Operator from qiskit.quantum_info.random import random_unitary from qiskit.test import QiskitTestCase from qiskit.extensions import UnitaryGate +from qiskit.transpiler import Target class TestUnroll3qOrMore(QiskitTestCase): @@ -91,3 +94,39 @@ def test_identity(self): after_dag = pass_.run(dag) after_circ = dag_to_circuit(after_dag) self.assertTrue(Operator(circuit).equiv(Operator(after_circ))) + + def test_target(self): + """Test target is respected by the unroll 3q or more pass.""" + target = Target(num_qubits=3) + target.add_instruction(CCXGate()) + qc = QuantumCircuit(3) + qc.ccx(0, 1, 2) + qc.append(RCCXGate(), [0, 1, 2]) + unroll_pass = Unroll3qOrMore(target=target) + res = unroll_pass(qc) + self.assertIn('ccx', res.count_ops()) + self.assertNotIn('rccx', res.count_ops()) + + def test_basis_gates(self): + """Test basis_gates are respected by the unroll 3q or more pass.""" + basis_gates = ["rccx"] + qc = QuantumCircuit(3) + qc.ccx(0, 1, 2) + qc.append(RCCXGate(), [0, 1, 2]) + unroll_pass = Unroll3qOrMore(basis_gates=basis_gates) + res = unroll_pass(qc) + self.assertNotIn('ccx', res.count_ops()) + self.assertIn('rccx', res.count_ops()) + + def test_target_over_basis_gates(self): + """Test target is respected over basis_gates by the unroll 3q or more pass.""" + target = Target(num_qubits=3) + basis_gates = ['rccx'] + target.add_instruction(CCXGate()) + qc = QuantumCircuit(3) + qc.ccx(0, 1, 2) + qc.append(RCCXGate(), [0, 1, 2]) + unroll_pass = Unroll3qOrMore(target=target, basis_gates=basis_gates) + res = unroll_pass(qc) + self.assertIn('ccx', res.count_ops()) + self.assertNotIn('rccx', res.count_ops()) From 4a79709ffea66d88f524159e7849fedde4fdd767 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 28 Apr 2022 18:13:09 -0400 Subject: [PATCH 2/4] Update releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml Co-authored-by: Naoki Kanazawa --- releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml index ecc8a655f3ef..608d6eefb263 100644 --- a/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml +++ b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml @@ -2,7 +2,7 @@ features: - | The constructor for the :class:`~.Unroll3qOrMore` transpiler pass has - two new optional keyword arguments, ``target`` and ``basis`` gates. These + two new optional keyword arguments, ``target`` and ``basis_bates``. These options enable you to specify the :class:`~.Target` or supported basis gates respectively to describe the target backend. If any of the operations in the circuit are in the ``target`` or ``basis_gates`` those will not From 5d9296d2fbf81976c864f8e69dc6187468af58bd Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 29 Apr 2022 16:13:42 -0400 Subject: [PATCH 3/4] Fix lint --- test/python/transpiler/test_unroll_3q_or_more.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py index c7d084246073..34181309ec4d 100644 --- a/test/python/transpiler/test_unroll_3q_or_more.py +++ b/test/python/transpiler/test_unroll_3q_or_more.py @@ -104,8 +104,8 @@ def test_target(self): qc.append(RCCXGate(), [0, 1, 2]) unroll_pass = Unroll3qOrMore(target=target) res = unroll_pass(qc) - self.assertIn('ccx', res.count_ops()) - self.assertNotIn('rccx', res.count_ops()) + self.assertIn("ccx", res.count_ops()) + self.assertNotIn("rccx", res.count_ops()) def test_basis_gates(self): """Test basis_gates are respected by the unroll 3q or more pass.""" @@ -115,18 +115,18 @@ def test_basis_gates(self): qc.append(RCCXGate(), [0, 1, 2]) unroll_pass = Unroll3qOrMore(basis_gates=basis_gates) res = unroll_pass(qc) - self.assertNotIn('ccx', res.count_ops()) - self.assertIn('rccx', res.count_ops()) + self.assertNotIn("ccx", res.count_ops()) + self.assertIn("rccx", res.count_ops()) def test_target_over_basis_gates(self): """Test target is respected over basis_gates by the unroll 3q or more pass.""" target = Target(num_qubits=3) - basis_gates = ['rccx'] + basis_gates = ["rccx"] target.add_instruction(CCXGate()) qc = QuantumCircuit(3) qc.ccx(0, 1, 2) qc.append(RCCXGate(), [0, 1, 2]) unroll_pass = Unroll3qOrMore(target=target, basis_gates=basis_gates) res = unroll_pass(qc) - self.assertIn('ccx', res.count_ops()) - self.assertNotIn('rccx', res.count_ops()) + self.assertIn("ccx", res.count_ops()) + self.assertNotIn("rccx", res.count_ops()) From f96ee7c6f80dfd76961d1997534cceef4e88824c Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 3 May 2022 16:32:31 +0100 Subject: [PATCH 4/4] Fix release-note typo --- releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml index 608d6eefb263..dd77da11cfa5 100644 --- a/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml +++ b/releasenotes/notes/unroll3q-target-bf57cc4365808862.yaml @@ -2,7 +2,7 @@ features: - | The constructor for the :class:`~.Unroll3qOrMore` transpiler pass has - two new optional keyword arguments, ``target`` and ``basis_bates``. These + two new optional keyword arguments, ``target`` and ``basis_gates``. These options enable you to specify the :class:`~.Target` or supported basis gates respectively to describe the target backend. If any of the operations in the circuit are in the ``target`` or ``basis_gates`` those will not