Skip to content

Commit 253d236

Browse files
Add topology_utils
1 parent 909991c commit 253d236

23 files changed

Lines changed: 3471 additions & 166 deletions

dwave/graphs/generators/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
from dwave.graphs.generators.markov import markov_network
1717
from dwave.graphs.generators.pegasus import *
1818
from dwave.graphs.generators.zephyr import *
19+
from dwave.graphs.generators.common import *

dwave/graphs/generators/chimera.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from networkx.algorithms.bipartite import color
2323
from networkx import diameter
2424

25-
from .common import _add_compatible_nodes, _add_compatible_edges, _add_compatible_terms
25+
from dwave.graphs.generators.common import _add_compatible_nodes, _add_compatible_edges, _add_compatible_terms
2626

2727
__all__ = ['chimera_graph',
2828
'chimera_coordinates',
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2026 D-Wave
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# ================================================================================================
16+
17+
from dwave.graphs.generators.common.common import (
18+
_add_compatible_edges,
19+
_add_compatible_nodes,
20+
_add_compatible_terms,
21+
)
22+
from dwave.graphs.generators.common.coord import *
23+
from dwave.graphs.generators.common.node_edge import *
24+
from dwave.graphs.generators.common.planeshift import *
25+
from dwave.graphs.generators.common.shape import *
26+
from dwave.graphs.generators.common.topology import *
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
# Copyright 2026 D-Wave
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# ================================================================================================
16+
117

218
def _add_compatible_edges(G, edge_list):
319
# Check edge_list defines a subgraph of G and create subgraph.
@@ -9,7 +25,8 @@ def _add_compatible_edges(G, edge_list):
925
G.remove_edges_from(list(G.edges))
1026
G.add_edges_from(edge_list)
1127
if G.number_of_edges() < len(edge_list):
12-
raise ValueError('edge_list contains duplicates.')
28+
raise ValueError("edge_list contains duplicates.")
29+
1330

1431
def _add_compatible_nodes(G, node_list):
1532
if node_list is not None:
@@ -19,11 +36,12 @@ def _add_compatible_nodes(G, node_list):
1936
remove_nodes = set(G) - nodes
2037
G.remove_nodes_from(remove_nodes)
2138
if G.number_of_nodes() < len(node_list):
22-
raise ValueError('node_list contains duplicates.')
23-
39+
raise ValueError("node_list contains duplicates.")
40+
41+
2442
def _add_compatible_terms(G, node_list, edge_list):
2543
_add_compatible_edges(G, edge_list)
2644
_add_compatible_nodes(G, node_list)
27-
#Check node deletion hasn't caused edge deletion:
45+
# Check node deletion hasn't caused edge deletion:
2846
if edge_list is not None and len(edge_list) != G.number_of_edges():
29-
raise ValueError('The edge_list contains nodes absent from the node_list')
47+
raise ValueError("The edge_list contains nodes absent from the node_list")
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Copyright 2026 D-Wave
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# ================================================================================================
16+
17+
18+
from __future__ import annotations
19+
20+
from enum import Enum
21+
from functools import total_ordering
22+
23+
from dwave.graphs.generators.common.shape import TopologyShape
24+
from dwave.graphs.tuplelike import TupleLike
25+
26+
__all__ = ["Coord", "CoordKind"]
27+
28+
29+
class CoordKind(Enum): # Kinds of coordinates of nodes in topologies
30+
CARTESIAN = 0
31+
TOPOLOGY = 1
32+
33+
34+
@total_ordering
35+
class Coord(TupleLike):
36+
"""A class to represent the coordinate of a topology node."""
37+
38+
__hash__ = TupleLike.__hash__
39+
40+
@classmethod
41+
def topology_name(cls) -> str:
42+
"""Returns the name of the topology associated with the class.
43+
44+
Raises:
45+
NotImplementedError: To be implemented in subclasses for
46+
specific topologies.
47+
48+
Returns:
49+
str: The name of the topology the class is designed for.
50+
"""
51+
raise NotImplementedError
52+
53+
@classmethod
54+
def kind(cls) -> CoordKind:
55+
"""Returns the kind of the coordinate associated with the class.
56+
57+
Raises:
58+
NotImplementedError: To be implemented in subclasses for
59+
specific coordinates.
60+
61+
Returns:
62+
CoordKind: The kind of class's coordinate.
63+
"""
64+
raise NotImplementedError
65+
66+
def __init__(self, *args, **kwargs) -> None:
67+
super().__init__(*args, **kwargs)
68+
69+
def _args_valid_topology(self, *args, **kwargs) -> None:
70+
"""Verifies the given coordinate is a valid coordinate."""
71+
raise NotImplementedError
72+
73+
def is_shape_consistent(self, shape: TopologyShape) -> bool:
74+
"""Tells whether the coordinate is consistent with a topology shape.
75+
76+
Args:
77+
shape: The shape to check the consistency of the coordinate with.
78+
79+
Returns:
80+
bool: Whether the coordinate is consistent with the shape.
81+
"""
82+
raise NotImplementedError
83+
84+
def is_quotient(self) -> bool:
85+
"""Whether the given coordinate is a quotient coordinate."""
86+
raise NotImplementedError
87+
88+
def to_quotient(
89+
self,
90+
) -> Coord:
91+
"""Converts the coordinate to its corresponding coordinate in a quotient graph."""
92+
raise NotImplementedError
93+
94+
def to_non_quotient(
95+
self,
96+
shape: TopologyShape,
97+
**kwargs,
98+
) -> list[Coord]:
99+
"""Expands the coordinate to a non-quotient shape; i.e. it gives
100+
all coordinates in a non-quotient graph whose quotient is the coordinate.
101+
102+
Args:
103+
shape: The non-quotient shape to expand the coordinate to.
104+
105+
Returns:
106+
list[Coord]: The expansion of the coordinate into non-quotient.
107+
"""
108+
raise NotImplementedError
109+
110+
def convert(self, coord_kind: CoordKind) -> Coord:
111+
"""Converts the coordinate to other kinds of coordinate in the same topology.
112+
113+
Args:
114+
coord_kind (CoordKind): The coordinate kind to convert the coordinate to.
115+
116+
Raises:
117+
NotImplementedError: To be implemented in subclasses for
118+
specific coordinates.
119+
120+
Returns:
121+
Coord: The converted coordinate.
122+
"""
123+
raise NotImplementedError
124+
125+
def __eq__(self, other: object) -> bool:
126+
if (type(self) is not type(other)) or (self.is_quotient() != other.is_quotient()):
127+
return NotImplemented
128+
return self._tuple_format == other._tuple_format
129+
130+
def __lt__(self, other: object) -> bool:
131+
if (type(self) is not type(other)) or (self.is_quotient() != other.is_quotient()):
132+
return NotImplemented
133+
return self._tuple_format < other._tuple_format

0 commit comments

Comments
 (0)