Skip to content

Commit fb13655

Browse files
authored
🩹⬆️ Enable support for newer rustworkx version and update pre-commit (#329)
This PR enables support for the newest `rustworkx` version (`0.13.0`) that has brought initial typing support and some other improvements. This broke some parts of the `subarchitectures` module in QMAP. Since these were breaking changes, the lower cap of the `rustworkx` dependency is raised to `>=0.13`. <!--pre-commit.ci start--> updates: - [github.com/charliermarsh/ruff-pre-commit: v0.0.270 → v0.0.272](astral-sh/ruff-pre-commit@v0.0.270...v0.0.272) <!--pre-commit.ci end-->
2 parents 65a82a3 + b540cc5 commit fb13655

File tree

7 files changed

+85
-66
lines changed

7 files changed

+85
-66
lines changed

‎.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ repos:
5151

5252
# Run ruff (subsumes pyupgrade, isort, flake8+plugins, and more)
5353
- repo: https://github.com/charliermarsh/ruff-pre-commit
54-
rev: v0.0.270
54+
rev: v0.0.272
5555
hooks:
5656
- id: ruff
5757
args: ["--fix"]
2.42 KB
Binary file not shown.

‎mqt/qmap/libs/rigetti_16.pickle

3.83 KB
Binary file not shown.

‎mqt/qmap/subarchitectures.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,26 @@
1515
import pickle
1616
from itertools import combinations
1717
from pathlib import Path
18-
from typing import TYPE_CHECKING, Dict, NewType, Set, Tuple
18+
from typing import TYPE_CHECKING, Dict, NewType, Optional, Set, Tuple
1919

2020
if TYPE_CHECKING: # pragma: no cover
21+
from collections.abc import Iterable
22+
2123
from matplotlib import figure
22-
from qiskit.providers import Backend
24+
from qiskit.providers import BackendV1
25+
from typing_extensions import TypeAlias
2326

2427
from mqt.qmap import Architecture
2528

2629

30+
import contextlib
31+
2732
import rustworkx as rx
33+
import rustworkx.visualization as rxviz
34+
35+
with contextlib.suppress(TypeError):
36+
Graph: TypeAlias = rx.PyGraph[int, Optional[int]]
37+
2838

2939
PartialOrder = NewType("PartialOrder", Dict[Tuple[int, int], Set[Tuple[int, int]]])
3040

@@ -40,7 +50,7 @@ class SubarchitectureOrder:
4050

4151
def __init__(self) -> None:
4252
"""Initialize a partial order."""
43-
self.arch: rx.PyGraph = rx.PyGraph()
53+
self.arch: Graph = rx.PyGraph()
4454
self.subarch_order: PartialOrder = PartialOrder({})
4555
self.desirable_subarchitectures: PartialOrder = PartialOrder({})
4656
self.isomorphisms: dict[tuple[int, int], dict[tuple[int, int], dict[int, int]]] = {}
@@ -50,7 +60,7 @@ def __init__(self) -> None:
5060
self.__compute_desirable_subarchitectures()
5161

5262
@classmethod
53-
def from_retworkx_graph(cls, graph: rx.PyGraph) -> SubarchitectureOrder:
63+
def from_retworkx_graph(cls, graph: Graph) -> SubarchitectureOrder:
5464
"""Construct the partial order from retworkx graph.
5565
5666
Args:
@@ -68,24 +78,24 @@ def from_retworkx_graph(cls, graph: rx.PyGraph) -> SubarchitectureOrder:
6878
return so
6979

7080
@classmethod
71-
def from_coupling_map(cls, coupling_map: set[tuple[int, int]] | list[tuple[int, int]]) -> SubarchitectureOrder:
81+
def from_coupling_map(cls, coupling_map: Iterable[tuple[int, int]]) -> SubarchitectureOrder:
7282
"""Construct partial order from coupling map defined as set of tuples of connected qubits.
7383
7484
Args:
75-
coupling_map: Set or list of tuples of connected qubits.
85+
coupling_map: Iterable of tuples of connected qubits.
7686
7787
Returns:
7888
The resulting partial order.
7989
"""
8090
num_nodes = max(max(int(u), int(v)) for u, v in coupling_map)
81-
graph = rx.PyGraph()
91+
graph: Graph = rx.PyGraph()
8292
graph.add_nodes_from(list(range(num_nodes + 1)))
83-
graph.add_edges_from_no_data([tuple(edge) for edge in coupling_map])
93+
graph.add_edges_from_no_data(list(coupling_map))
8494

8595
return cls.from_retworkx_graph(graph)
8696

8797
@classmethod
88-
def from_backend(cls, backend: Backend) -> SubarchitectureOrder:
98+
def from_backend(cls, backend: BackendV1) -> SubarchitectureOrder:
8999
"""Construct the partial order from a coupling map defined as a Qiskit backend.
90100
91101
Args:
@@ -144,7 +154,7 @@ def from_string(cls, name: str) -> SubarchitectureOrder:
144154
return cls.from_library(lib_path)
145155
return SubarchitectureOrder()
146156

147-
def optimal_candidates(self, nqubits: int) -> list[rx.PyGraph]:
157+
def optimal_candidates(self, nqubits: int) -> list[Graph]:
148158
"""Return optimal subarchitecture candidate.
149159
150160
Args:
@@ -176,7 +186,7 @@ def optimal_candidates(self, nqubits: int) -> list[rx.PyGraph]:
176186

177187
return [self.sgs[n][i] for (n, i) in opt_cands]
178188

179-
def covering(self, nqubits: int, size: int) -> list[rx.PyGraph]:
189+
def covering(self, nqubits: int, size: int) -> list[Graph]:
180190
"""Return covering for nqubit circuits.
181191
182192
Args:
@@ -218,7 +228,7 @@ def store_library(self, lib_name: str | Path) -> None:
218228
with path.open("wb") as f:
219229
pickle.dump(self, f)
220230

221-
def draw_subarchitecture(self, subarchitecture: rx.PyGraph | tuple[int, int]) -> figure.Figure:
231+
def draw_subarchitecture(self, subarchitecture: Graph | tuple[int, int]) -> figure.Figure:
222232
"""Create a matplotlib figure showing subarchitecture within the entire architecture.
223233
224234
Nodes that are part of the subarchitecture are drawn yellow.
@@ -235,9 +245,9 @@ def draw_subarchitecture(self, subarchitecture: rx.PyGraph | tuple[int, int]) ->
235245
colors = [SubarchitectureOrder.inactive_color for _ in range(self.arch.num_nodes())]
236246
for node in subarchitecture.nodes():
237247
colors[node] = SubarchitectureOrder.active_color
238-
return rx.visualization.mpl_draw(subarchitecture, node_color=colors)
248+
return rxviz.mpl_draw(self.arch, node_color=colors) # type: ignore[no-untyped-call]
239249

240-
def draw_subarchitectures(self, subarchitectures: list[rx.PyGraph] | list[tuple[int, int]]) -> list[figure.Figure]:
250+
def draw_subarchitectures(self, subarchitectures: list[Graph] | list[tuple[int, int]]) -> list[figure.Figure]:
241251
"""Create matplotlib figures showing subarchitectures within the entire architecture.
242252
243253
For each subarchitecture one figure is drawn.
@@ -254,15 +264,15 @@ def draw_subarchitectures(self, subarchitectures: list[rx.PyGraph] | list[tuple[
254264

255265
def __compute_subarchs(self) -> None:
256266
"""Compute all subarchitectures of the architecture."""
257-
self.sgs: list[list[rx.PyGraph]] = [[] for i in range(self.arch.num_nodes() + 1)]
267+
self.sgs: list[list[Graph]] = [[] for i in range(self.arch.num_nodes() + 1)]
258268

259269
for i in range(1, self.arch.num_nodes() + 1):
260270
node_combinations = combinations(range(self.arch.num_nodes()), i)
261271
for sg in (self.arch.subgraph(selected_nodes) for selected_nodes in node_combinations):
262-
if rx.is_connected(sg):
272+
if rx.is_connected(sg): # type: ignore[attr-defined]
263273
new_class = True
264274
for g in self.sgs[i]:
265-
if rx.is_isomorphic(g, sg):
275+
if rx.is_isomorphic(g, sg): # type: ignore[attr-defined]
266276
new_class = False
267277
break
268278
if new_class:
@@ -279,7 +289,7 @@ def __compute_subarch_order(self) -> None:
279289
for n, sgs_n in enumerate(self.sgs[:-1]):
280290
for i, sg in enumerate(sgs_n):
281291
for j, parent_sg in enumerate(self.sgs[n + 1]):
282-
matcher = rx.graph_vf2_mapping(parent_sg, sg, subgraph=True)
292+
matcher = rx.graph_vf2_mapping(parent_sg, sg, subgraph=True) # type: ignore[attr-defined]
283293
for iso in matcher:
284294
self.subarch_order[(n, i)].add((n + 1, j))
285295
iso_rev = {}
@@ -354,8 +364,8 @@ def __path_order_less(self, n: int, i: int, n_prime: int, i_prime: int) -> bool:
354364
if v is w:
355365
continue
356366
if (
357-
rx.dijkstra_shortest_path_lengths(lhs, v, lambda _x: 1, goal=w)[w]
358-
> rx.dijkstra_shortest_path_lengths(rhs, iso[v], lambda _x: 1, goal=iso[w])[iso[w]]
367+
rx.dijkstra_shortest_path_lengths(lhs, v, lambda _x: 1, goal=w)[w] # type: ignore[attr-defined]
368+
> rx.dijkstra_shortest_path_lengths(rhs, iso[v], lambda _x: 1, goal=iso[w])[iso[w]] # type: ignore[attr-defined]
359369
):
360370
return True
361371
return False

‎pyproject.toml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ classifiers=[
3939
requires-python = ">=3.8"
4040
dependencies = [
4141
"qiskit-terra>=0.20.2",
42-
"rustworkx[all]>=0.12.0",
42+
"rustworkx[all]>=0.13.0",
4343
"importlib_resources>=5.0; python_version < '3.10'",
44+
"typing_extensions>=4.0",
4445
]
4546
dynamic = ["version"]
4647

@@ -105,11 +106,8 @@ testpaths = ["test/python"]
105106
addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"]
106107
log_cli_level = "INFO"
107108
xfail_strict = true
108-
filterwarnings = [
109-
"error",
110-
# See https://github.com/Qiskit/rustworkx/pull/728
111-
'ignore:RetworkxLoader.exec_module\(\) not found; falling back to load_module\(\):ImportWarning',
112-
]
109+
filterwarnings = ["error"]
110+
113111
[tool.coverage.run]
114112
source = ["mqt.qmap"]
115113

@@ -131,7 +129,7 @@ warn_unreachable = true
131129
explicit_package_bases = true
132130

133131
[[tool.mypy.overrides]]
134-
module = ["qiskit.*", "rustworkx.*", "matplotlib.*"]
132+
module = ["qiskit.*", "matplotlib.*"]
135133
ignore_missing_imports = true
136134

137135
[tool.pylint]

‎setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ def build_extension(self, ext: CMakeExtension) -> None:
5858
cmake_args += ["-GNinja"]
5959
else:
6060
# Single config generators are handled "normally"
61-
single_config = any(x in cmake_generator for x in {"NMake", "Ninja"})
61+
single_config = any(x in cmake_generator for x in ("NMake", "Ninja"))
6262
# CMake allows an arch-in-generator style for backward compatibility
63-
contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"})
63+
contains_arch = any(x in cmake_generator for x in ("ARM", "Win64"))
6464
# Convert distutils Windows platform specifiers to CMake -A arguments
6565
plat_to_cmake = {
6666
"win32": "Win32",

0 commit comments

Comments
 (0)