Skip to content

Commit

Permalink
Deprecate the BIPMapping transpiler pass in favor of external plugin (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
mtreinish authored Apr 12, 2023
1 parent 6392665 commit 602d340
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 24 deletions.
7 changes: 7 additions & 0 deletions qiskit/transpiler/passes/routing/algorithms/bip_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand All @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions qiskit/transpiler/passes/routing/bip_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -63,6 +64,12 @@ class BIPMapping(TransformationPass):
`arXiv:2106.06446 <https://arxiv.org/abs/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,
Expand Down
14 changes: 14 additions & 0 deletions releasenotes/notes/deprecate-bip-mapping-f0025c4c724e1ec8.yaml
Original file line number Diff line number Diff line change
@@ -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`.
64 changes: 40 additions & 24 deletions test/python/transpiler/test_bip_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)

Expand All @@ -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))
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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."""
Expand All @@ -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:
Expand All @@ -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."""
Expand All @@ -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)
Expand Down

0 comments on commit 602d340

Please sign in to comment.