Skip to content

Commit

Permalink
🩹⬆️ Enable support for newer rustworkx version and update `pre-comm…
Browse files Browse the repository at this point in the history
…it` (#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-->
  • Loading branch information
burgholzer authored Jun 15, 2023
2 parents 65a82a3 + b540cc5 commit fb13655
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ repos:

# Run ruff (subsumes pyupgrade, isort, flake8+plugins, and more)
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.270
rev: v0.0.272
hooks:
- id: ruff
args: ["--fix"]
Expand Down
Binary file modified mqt/qmap/libs/ibm_guadalupe_16.pickle
Binary file not shown.
Binary file modified mqt/qmap/libs/rigetti_16.pickle
Binary file not shown.
50 changes: 30 additions & 20 deletions mqt/qmap/subarchitectures.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,26 @@
import pickle
from itertools import combinations
from pathlib import Path
from typing import TYPE_CHECKING, Dict, NewType, Set, Tuple
from typing import TYPE_CHECKING, Dict, NewType, Optional, Set, Tuple

if TYPE_CHECKING: # pragma: no cover
from collections.abc import Iterable

from matplotlib import figure
from qiskit.providers import Backend
from qiskit.providers import BackendV1
from typing_extensions import TypeAlias

from mqt.qmap import Architecture


import contextlib

import rustworkx as rx
import rustworkx.visualization as rxviz

with contextlib.suppress(TypeError):
Graph: TypeAlias = rx.PyGraph[int, Optional[int]]


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

Expand All @@ -40,7 +50,7 @@ class SubarchitectureOrder:

def __init__(self) -> None:
"""Initialize a partial order."""
self.arch: rx.PyGraph = rx.PyGraph()
self.arch: Graph = rx.PyGraph()
self.subarch_order: PartialOrder = PartialOrder({})
self.desirable_subarchitectures: PartialOrder = PartialOrder({})
self.isomorphisms: dict[tuple[int, int], dict[tuple[int, int], dict[int, int]]] = {}
Expand All @@ -50,7 +60,7 @@ def __init__(self) -> None:
self.__compute_desirable_subarchitectures()

@classmethod
def from_retworkx_graph(cls, graph: rx.PyGraph) -> SubarchitectureOrder:
def from_retworkx_graph(cls, graph: Graph) -> SubarchitectureOrder:
"""Construct the partial order from retworkx graph.
Args:
Expand All @@ -68,24 +78,24 @@ def from_retworkx_graph(cls, graph: rx.PyGraph) -> SubarchitectureOrder:
return so

@classmethod
def from_coupling_map(cls, coupling_map: set[tuple[int, int]] | list[tuple[int, int]]) -> SubarchitectureOrder:
def from_coupling_map(cls, coupling_map: Iterable[tuple[int, int]]) -> SubarchitectureOrder:
"""Construct partial order from coupling map defined as set of tuples of connected qubits.
Args:
coupling_map: Set or list of tuples of connected qubits.
coupling_map: Iterable of tuples of connected qubits.
Returns:
The resulting partial order.
"""
num_nodes = max(max(int(u), int(v)) for u, v in coupling_map)
graph = rx.PyGraph()
graph: Graph = rx.PyGraph()
graph.add_nodes_from(list(range(num_nodes + 1)))
graph.add_edges_from_no_data([tuple(edge) for edge in coupling_map])
graph.add_edges_from_no_data(list(coupling_map))

return cls.from_retworkx_graph(graph)

@classmethod
def from_backend(cls, backend: Backend) -> SubarchitectureOrder:
def from_backend(cls, backend: BackendV1) -> SubarchitectureOrder:
"""Construct the partial order from a coupling map defined as a Qiskit backend.
Args:
Expand Down Expand Up @@ -144,7 +154,7 @@ def from_string(cls, name: str) -> SubarchitectureOrder:
return cls.from_library(lib_path)
return SubarchitectureOrder()

def optimal_candidates(self, nqubits: int) -> list[rx.PyGraph]:
def optimal_candidates(self, nqubits: int) -> list[Graph]:
"""Return optimal subarchitecture candidate.
Args:
Expand Down Expand Up @@ -176,7 +186,7 @@ def optimal_candidates(self, nqubits: int) -> list[rx.PyGraph]:

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

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

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

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

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

for i in range(1, self.arch.num_nodes() + 1):
node_combinations = combinations(range(self.arch.num_nodes()), i)
for sg in (self.arch.subgraph(selected_nodes) for selected_nodes in node_combinations):
if rx.is_connected(sg):
if rx.is_connected(sg): # type: ignore[attr-defined]
new_class = True
for g in self.sgs[i]:
if rx.is_isomorphic(g, sg):
if rx.is_isomorphic(g, sg): # type: ignore[attr-defined]
new_class = False
break
if new_class:
Expand All @@ -279,7 +289,7 @@ def __compute_subarch_order(self) -> None:
for n, sgs_n in enumerate(self.sgs[:-1]):
for i, sg in enumerate(sgs_n):
for j, parent_sg in enumerate(self.sgs[n + 1]):
matcher = rx.graph_vf2_mapping(parent_sg, sg, subgraph=True)
matcher = rx.graph_vf2_mapping(parent_sg, sg, subgraph=True) # type: ignore[attr-defined]
for iso in matcher:
self.subarch_order[(n, i)].add((n + 1, j))
iso_rev = {}
Expand Down Expand Up @@ -354,8 +364,8 @@ def __path_order_less(self, n: int, i: int, n_prime: int, i_prime: int) -> bool:
if v is w:
continue
if (
rx.dijkstra_shortest_path_lengths(lhs, v, lambda _x: 1, goal=w)[w]
> rx.dijkstra_shortest_path_lengths(rhs, iso[v], lambda _x: 1, goal=iso[w])[iso[w]]
rx.dijkstra_shortest_path_lengths(lhs, v, lambda _x: 1, goal=w)[w] # type: ignore[attr-defined]
> rx.dijkstra_shortest_path_lengths(rhs, iso[v], lambda _x: 1, goal=iso[w])[iso[w]] # type: ignore[attr-defined]
):
return True
return False
Expand Down
12 changes: 5 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ classifiers=[
requires-python = ">=3.8"
dependencies = [
"qiskit-terra>=0.20.2",
"rustworkx[all]>=0.12.0",
"rustworkx[all]>=0.13.0",
"importlib_resources>=5.0; python_version < '3.10'",
"typing_extensions>=4.0",
]
dynamic = ["version"]

Expand Down Expand Up @@ -105,11 +106,8 @@ testpaths = ["test/python"]
addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"]
log_cli_level = "INFO"
xfail_strict = true
filterwarnings = [
"error",
# See https://github.com/Qiskit/rustworkx/pull/728
'ignore:RetworkxLoader.exec_module\(\) not found; falling back to load_module\(\):ImportWarning',
]
filterwarnings = ["error"]

[tool.coverage.run]
source = ["mqt.qmap"]

Expand All @@ -131,7 +129,7 @@ warn_unreachable = true
explicit_package_bases = true

[[tool.mypy.overrides]]
module = ["qiskit.*", "rustworkx.*", "matplotlib.*"]
module = ["qiskit.*", "matplotlib.*"]
ignore_missing_imports = true

[tool.pylint]
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ def build_extension(self, ext: CMakeExtension) -> None:
cmake_args += ["-GNinja"]
else:
# Single config generators are handled "normally"
single_config = any(x in cmake_generator for x in {"NMake", "Ninja"})
single_config = any(x in cmake_generator for x in ("NMake", "Ninja"))
# CMake allows an arch-in-generator style for backward compatibility
contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"})
contains_arch = any(x in cmake_generator for x in ("ARM", "Win64"))
# Convert distutils Windows platform specifiers to CMake -A arguments
plat_to_cmake = {
"win32": "Win32",
Expand Down
Loading

0 comments on commit fb13655

Please sign in to comment.