From 1da614bc9505dd4959246a90353e54e9e10bd9cb Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 12 Apr 2023 06:38:18 -0400 Subject: [PATCH] Deprecate the BIPMapping transpiler pass in favor of external plugin (#9924) * Deprecate the BIPMapping transpiler pass in favor of external plugin This commit deprecates the BIPMapping transpiler pass. Since its introduction the pass has been in a weird state since it's introduction. It is a standalone transpiler pass that we never integrated it into transpile() because it has an external dependency on cplex which is a proprietary software package that most people don't have access too. With the introduction of the transpiler stage plugin interface the pass has been turned into an external package: https://github.com/qiskit-community/qiskit-bip-mapper By using the plugin interface the pass can now be cleanly integrates into the transpile() function and also makes the requirement to have cplex installed much more explicit. For users with cplex it's much easier to run the BIPMapping pass as part of a transpilation workflow with `transpile(..., routing_method="bip")`. Closes #8662 * Catch deprecation warnings in tests * Fix lint --- .../passes/routing/algorithms/bip_model.py | 7 ++ .../transpiler/passes/routing/bip_mapping.py | 7 ++ ...eprecate-bip-mapping-f0025c4c724e1ec8.yaml | 14 ++++ test/python/transpiler/test_bip_mapping.py | 64 ++++++++++++------- 4 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/deprecate-bip-mapping-f0025c4c724e1ec8.yaml diff --git a/qiskit/transpiler/passes/routing/algorithms/bip_model.py b/qiskit/transpiler/passes/routing/algorithms/bip_model.py index f0b7d576d4c3..7a37ff6a949b 100644 --- a/qiskit/transpiler/passes/routing/algorithms/bip_model.py +++ b/qiskit/transpiler/passes/routing/algorithms/bip_model.py @@ -26,6 +26,7 @@ trace_to_fid, ) from qiskit.utils import optionals as _optionals +from qiskit.utils.deprecation import deprecate_func logger = logging.getLogger(__name__) @@ -41,6 +42,12 @@ class BIPMappingModel: the solution will be stored in :attr:`solution`). None if it's not yet set. """ + @deprecate_func( + since="0.24.0", + additional_msg="This has been replaced by a new transpiler plugin package: " + "qiskit-bip-mapper. More details can be found here: " + "https://github.com/qiskit-community/qiskit-bip-mapper", + ) # pylint: disable=bad-docstring-quotes def __init__(self, dag, coupling_map, qubit_subset, dummy_timesteps=None): """ Args: diff --git a/qiskit/transpiler/passes/routing/bip_mapping.py b/qiskit/transpiler/passes/routing/bip_mapping.py index 9f6fc177eece..58b040a9821c 100644 --- a/qiskit/transpiler/passes/routing/bip_mapping.py +++ b/qiskit/transpiler/passes/routing/bip_mapping.py @@ -23,6 +23,7 @@ from qiskit.transpiler.exceptions import TranspilerError from qiskit.transpiler.passes.routing.algorithms.bip_model import BIPMappingModel from qiskit.transpiler.target import target_to_backend_properties, Target +from qiskit.utils.deprecation import deprecate_func logger = logging.getLogger(__name__) @@ -63,6 +64,12 @@ class BIPMapping(TransformationPass): `arXiv:2106.06446 `_ """ + @deprecate_func( + since="0.24.0", + additional_msg="This has been replaced by a new transpiler plugin package: " + "qiskit-bip-mapper. More details can be found here: " + "https://github.com/qiskit-community/qiskit-bip-mapper", + ) # pylint: disable=bad-docstring-quotes def __init__( self, coupling_map, diff --git a/releasenotes/notes/deprecate-bip-mapping-f0025c4c724e1ec8.yaml b/releasenotes/notes/deprecate-bip-mapping-f0025c4c724e1ec8.yaml new file mode 100644 index 000000000000..93a0523397bd --- /dev/null +++ b/releasenotes/notes/deprecate-bip-mapping-f0025c4c724e1ec8.yaml @@ -0,0 +1,14 @@ +--- +deprecations: + - | + The transpiler routing pass, :class:`~.BIPMapping` has been deprecated + and will be removed in a future release. It has been replaced by an external + plugin package: ``qiskit-bip-mapper``. Details for this new package can + be found at the package's github repository: + + https://github.com/qiskit-community/qiskit-bip-mapper + + The pass was made into a separate plugin package for two reasons, first + the dependency on CPLEX makes it harder to use and secondly the plugin + packge more cleanly integrates with :func:`~.transpile`. + diff --git a/test/python/transpiler/test_bip_mapping.py b/test/python/transpiler/test_bip_mapping.py index 60283abec750..a95ca727f79a 100644 --- a/test/python/transpiler/test_bip_mapping.py +++ b/test/python/transpiler/test_bip_mapping.py @@ -36,7 +36,8 @@ def test_empty(self): """Returns the original circuit if the circuit is empty.""" coupling = CouplingMap([[0, 1]]) circuit = QuantumCircuit(2) - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) self.assertEqual(circuit, actual) def test_no_two_qubit_gates(self): @@ -49,8 +50,8 @@ def test_no_two_qubit_gates(self): circuit = QuantumCircuit(2) circuit.h(0) - - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) self.assertEqual(circuit, actual) @@ -70,7 +71,8 @@ def test_trivial_case(self): circuit.h(0) circuit.cx(2, 0) - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) self.assertEqual(3, len(actual)) for inst, _, _ in actual.data: # there are no swaps self.assertFalse(isinstance(inst, SwapGate)) @@ -82,7 +84,8 @@ def test_no_swap(self): circuit = QuantumCircuit(3) circuit.cx(1, 2) - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) q = QuantumRegister(3, name="q") expected = QuantumCircuit(q) @@ -98,7 +101,8 @@ def test_ignore_initial_layout(self): circuit.cx(1, 2) property_set = {"layout": Layout.generate_trivial_layout(*circuit.qubits)} - actual = BIPMapping(coupling)(circuit, property_set) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit, property_set) q = QuantumRegister(3, name="q") expected = QuantumCircuit(q) @@ -117,7 +121,8 @@ def test_can_map_measurements_correctly(self): circuit.measure(qr[1], cr[0]) circuit.measure(qr[2], cr[1]) - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) q = QuantumRegister(3, "q") expected = QuantumCircuit(q, cr) @@ -139,7 +144,8 @@ def test_can_map_measurements_correctly_with_target(self): circuit.measure(qr[1], cr[0]) circuit.measure(qr[2], cr[1]) - actual = BIPMapping(target)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(target)(circuit) q = QuantumRegister(3, "q") expected = QuantumCircuit(q, cr) @@ -162,8 +168,9 @@ def test_never_modify_mapped_circuit(self): circuit.measure(2, 1) dag = circuit_to_dag(circuit) - mapped_dag = BIPMapping(coupling).run(dag) - remapped_dag = BIPMapping(coupling).run(mapped_dag) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + mapped_dag = BIPMapping(coupling).run(dag) + remapped_dag = BIPMapping(coupling).run(mapped_dag) self.assertEqual(mapped_dag, remapped_dag) @@ -177,7 +184,8 @@ def test_no_swap_multi_layer(self): circuit.cx(qr[0], qr[3]) property_set = {} - actual = BIPMapping(coupling, objective="depth")(circuit, property_set) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling, objective="depth")(circuit, property_set) self.assertEqual(2, actual.depth()) CheckMap(coupling)(actual, property_set) @@ -194,7 +202,8 @@ def test_unmappable_cnots_in_a_layer(self): circuit.measure(qr, cr) coupling = CouplingMap([[0, 1], [1, 2], [1, 3]]) # {0: [1], 1: [2, 3]} - actual = BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling)(circuit) # Fails to map and returns the original circuit self.assertEqual(circuit, actual) @@ -233,7 +242,8 @@ def test_multi_cregs(self): coupling = CouplingMap([[0, 1], [0, 2], [2, 3]]) # linear [1, 0, 2, 3] property_set = {} - actual = BIPMapping(coupling, objective="depth")(circuit, property_set) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling, objective="depth")(circuit, property_set) self.assertEqual(5, actual.depth()) CheckMap(coupling)(actual, property_set) @@ -264,7 +274,8 @@ def test_swaps_in_dummy_steps(self): coupling = CouplingMap.from_line(4) property_set = {} - actual = BIPMapping(coupling, objective="depth")(circuit, property_set) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling, objective="depth")(circuit, property_set) self.assertEqual(7, actual.depth()) CheckMap(coupling)(actual, property_set) @@ -295,7 +306,8 @@ def test_different_number_of_virtual_and_physical_qubits(self): coupling = CouplingMap.from_line(5) with self.assertRaises(TranspilerError): - BIPMapping(coupling)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + BIPMapping(coupling)(circuit) def test_qubit_subset(self): """Test if `qubit_subset` option works as expected.""" @@ -306,7 +318,8 @@ def test_qubit_subset(self): coupling = CouplingMap([(0, 1), (1, 3), (3, 2)]) qubit_subset = [0, 1, 3] - actual = BIPMapping(coupling, qubit_subset=qubit_subset)(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + actual = BIPMapping(coupling, qubit_subset=qubit_subset)(circuit) # all used qubits are in qubit_subset bit_indices = {bit: index for index, bit in enumerate(actual.qubits)} for _, qargs, _ in actual.data: @@ -323,7 +336,8 @@ def test_unconnected_qubit_subset(self): coupling = CouplingMap([(0, 1), (1, 3), (3, 2)]) with self.assertRaises(TranspilerError): - BIPMapping(coupling, qubit_subset=[0, 1, 2])(circuit) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + BIPMapping(coupling, qubit_subset=[0, 1, 2])(circuit) def test_objective_function(self): """Test if ``objective`` functions prioritize metrics correctly.""" @@ -345,13 +359,15 @@ def test_objective_function(self): qc.dcx(0, 1) qc.cx(2, 3) coupling = CouplingMap(FakeLima().configuration().coupling_map) - dep_opt = BIPMapping(coupling, objective="depth", qubit_subset=[0, 1, 3, 4])(qc) - err_opt = BIPMapping( - coupling, - objective="gate_error", - qubit_subset=[0, 1, 3, 4], - backend_prop=FakeLima().properties(), - )(qc) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + dep_opt = BIPMapping(coupling, objective="depth", qubit_subset=[0, 1, 3, 4])(qc) + with self.assertWarnsRegex(DeprecationWarning, r"^The class.*is deprecated"): + err_opt = BIPMapping( + coupling, + objective="gate_error", + qubit_subset=[0, 1, 3, 4], + backend_prop=FakeLima().properties(), + )(qc) # depth = number of su4 layers (mirrored gates have to be consolidated as single su4 gates) pm_ = PassManager([Collect2qBlocks(), ConsolidateBlocks(basis_gates=["cx", "u"])]) dep_opt = pm_.run(dep_opt)