qoolqit.graphs
graphs
Section titled “
graphs
”Graph creation and manipulation in QoolQit.
Modules:
-
base_graph– -
data_graph– -
utils–
Classes:
-
BaseGraph–The BaseGraph in QoolQit, directly inheriting from the NetworkX Graph.
-
DataGraph–The main graph structure to represent problem data.
Functions:
-
all_node_pairs–Return all pairs of nodes (u, v) where u < v.
-
distances–Return a dictionary of edge distances.
-
random_coords–Generate a random set of node coordinates on a square of side L.
-
random_edge_list–Generates a random set of k edges linkings items from a set of nodes.
-
scale_coords–Scale the coordinates by a given value.
-
space_coords–Spaces the coordinates so the minimum distance is equal to a set spacing.
BaseGraph(edges: Iterable = [])
Section titled “
BaseGraph(edges: Iterable = [])
”The BaseGraph in QoolQit, directly inheriting from the NetworkX Graph.
Defines basic functionalities for graphs within the Rydberg Analog, such as instantiating from a set of node coordinates, directly accessing node distances, and checking if the graph is unit-disk.
Parameters:
edges
Section titled “ edges
”Iterable, default:[])
–set of edge tuples (i, j)
Methods:
-
distances–Returns a dictionary of distances for a given set of edges.
-
draw–Draw the graph.
-
from_coordinates–Construct a base graph from a set of coordinates.
-
from_nodes–Construct a base graph from a set of nodes.
-
from_nx–Convert a NetworkX Graph object into a QoolQit graph instance.
-
interactions–Rydberg model interaction 1/r^6 between pair of nodes.
-
is_ud_graph–Check if the graph is unit-disk.
-
max_distance–Returns the maximum distance in the graph.
-
min_distance–Returns the minimum distance in the graph.
-
rescale_coords–Rescales the node coordinates by a factor.
-
set_ud_edges–Reset the set of edges to be equal to the set of unit-disk edges.
-
ud_edges–Returns the set of edges given by the intersection of circles of a given radius.
-
ud_radius_range–Return the range (R_min, R_max) where the graph is unit-disk.
Attributes:
-
all_node_pairs(set) –Return a list of all possible node pairs in the graph.
-
coords(dict) –Return the dictionary of node coordinates.
-
has_coords(bool) –Check if the graph has coordinates.
-
has_edge_weights(bool) –Check if the graph has edge weights.
-
has_edges(bool) –Check if the graph has edges.
-
has_node_weights(bool) –Check if the graph has node weights.
-
sorted_edges(set) –Returns the set of edges (u, v) such that (u < v).
Source code in qoolqit/graphs/base_graph.py
def __init__(self, edges: Iterable = []) -> None: """ Default constructor for the BaseGraph.
Arguments: edges: set of edge tuples (i, j) """ if edges and not isinstance(edges, Iterable): raise TypeError("Input is not a valid edge list.")
super().__init__() self.add_edges_from(edges) self._coords = {i: None for i in self.nodes} self._reset_dicts()
all_node_pairs: set
property
Section titled “
all_node_pairs: set
property
”Return a list of all possible node pairs in the graph.
coords: dict
property
writable
Section titled “
coords: dict
property
writable
”Return the dictionary of node coordinates.
has_coords: bool
property
Section titled “
has_coords: bool
property
”Check if the graph has coordinates.
Requires all nodes to have coordinates.
has_edge_weights: bool
property
Section titled “
has_edge_weights: bool
property
”Check if the graph has edge weights.
Requires all edges to have a weight.
has_edges: bool
property
Section titled “
has_edges: bool
property
”Check if the graph has edges.
has_node_weights: bool
property
Section titled “
has_node_weights: bool
property
”Check if the graph has node weights.
Requires all nodes to have a weight.
sorted_edges: set
property
Section titled “
sorted_edges: set
property
”Returns the set of edges (u, v) such that (u < v).
distances(edge_list: Iterable | None = None) -> dict
Section titled “
distances(edge_list: Iterable | None = None) -> dict
”Returns a dictionary of distances for a given set of edges.
Distances are calculated directly from the coordinates. Raises an error if there are no coordinates on the graph.
Parameters:
edge_list
Section titled “ edge_list
”Iterable | None, default:None)
–set of edges.
Source code in qoolqit/graphs/base_graph.py
def distances(self, edge_list: Iterable | None = None) -> dict: """Returns a dictionary of distances for a given set of edges.
Distances are calculated directly from the coordinates. Raises an error if there are no coordinates on the graph.
Arguments: edge_list: set of edges. """ if self.has_coords: if edge_list is None: edge_list = self.all_node_pairs elif len(edge_list) == 0: # type: ignore [arg-type] raise ValueError("Trying to compute distances for an empty edge list.") return distances(self.coords, edge_list) else: raise AttributeError("Trying to compute distances for a graph without coordinates.")
draw(ax: Axes | None = None, **kwargs: Any) -> None
Section titled “
draw(ax: Axes | None = None, **kwargs: Any) -> None
”Draw the graph.
Uses the draw_networkx function from NetworkX.
Parameters:
(Axes | None, default:None)
–Axes object to draw on. If None, uses the current Axes.
**kwargs
Section titled “ **kwargs
”Any, default:{})
–keyword-arguments to pass to draw_networkx.
Source code in qoolqit/graphs/base_graph.py
def draw(self, ax: Axes | None = None, **kwargs: Any) -> None: """Draw the graph.
Uses the draw_networkx function from NetworkX.
Args: ax: Axes object to draw on. If None, uses the current Axes. **kwargs: keyword-arguments to pass to draw_networkx. """ if self.has_coords: if "hide_ticks" not in kwargs: kwargs["hide_ticks"] = False
nx.draw_networkx(self, pos=self.coords, ax=ax, **kwargs)
if ax is None: ax = plt.gca() ax.set_xlabel("x") ax.set_ylabel("y") ax.grid(True, color="lightgray", linestyle="--", linewidth=0.7)
# minimum ybox ylim = ax.get_ylim() if (ylim[1] - ylim[0]) < 2: y_center = (ylim[0] + ylim[1]) / 2 ax.set_ylim(y_center - 1, y_center + 1) plt.tight_layout() else: nx.draw_networkx(self, ax=ax, **kwargs)
from_coordinates(coords: list | dict) -> BaseGraph
classmethod
Section titled “
from_coordinates(coords: list | dict) -> BaseGraph
classmethod
”Construct a base graph from a set of coordinates.
Parameters:
coords
Section titled “ coords
”list | dict)
–list or dictionary of coordinate pairs.
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_coordinates(cls, coords: list | dict) -> BaseGraph: """Construct a base graph from a set of coordinates.
Arguments: coords: list or dictionary of coordinate pairs. """ if isinstance(coords, list): nodes = list(range(len(coords))) coords_dict = {i: pos for i, pos in enumerate(coords)} elif isinstance(coords, dict): nodes = list(coords.keys()) coords_dict = coords graph = cls.from_nodes(nodes) graph._coords = coords_dict graph._reset_dicts() return graph
from_nodes(nodes: Iterable) -> BaseGraph
classmethod
Section titled “
from_nodes(nodes: Iterable) -> BaseGraph
classmethod
”Construct a base graph from a set of nodes.
Parameters:
nodes
Section titled “ nodes
”Iterable)
–set of nodes.
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_nodes(cls, nodes: Iterable) -> BaseGraph: """Construct a base graph from a set of nodes.
Arguments: nodes: set of nodes. """ graph = cls() graph.add_nodes_from(nodes) graph._coords = {i: None for i in graph.nodes} graph._reset_dicts() return graph
from_nx(g: nx.Graph) -> BaseGraph
classmethod
Section titled “
from_nx(g: nx.Graph) -> BaseGraph
classmethod
”Convert a NetworkX Graph object into a QoolQit graph instance.
The input networkx.Graph graph must be defined only with the following allowed
Node attributes
Edge attributes: weight: represents the edge weight. Must be a real number.
Returns an instance of the class with following attributes
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_nx(cls, g: nx.Graph) -> BaseGraph: """Convert a NetworkX Graph object into a QoolQit graph instance.
The input `networkx.Graph` graph must be defined only with the following allowed
Node attributes: pos (tuple): represents the node 2D position. Must be a list/tuple of real numbers. weight: represents the node weight. Must be a real number. Edge attributes: weight: represents the edge weight. Must be a real number.
Returns an instance of the class with following attributes: - _node_weights : dict[node, float or None] - _edge_weights : dict[(u,v), float or None] - _coords : dict[node, (float,float) or None] """ if not isinstance(g, nx.Graph): raise TypeError("Input must be a networkx.Graph instance.")
g = nx.convert_node_labels_to_integers(g) num_nodes = len(g.nodes) num_edges = len(g.edges)
# validate node attributes for name, data in g.nodes.data(): unexpected_keys = set(data) - {"weight", "pos"} if unexpected_keys: raise ValueError(f"{unexpected_keys} not allowed in node attributes.")
node_pos = nx.get_node_attributes(g, "pos") if node_pos: if len(node_pos) != num_nodes: raise ValueError("Node attribute `pos` must be defined for all nodes") for name, pos in node_pos.items(): is_2D = isinstance(pos, (tuple, list)) & (len(pos) == 2) is_real = all(isinstance(p, (float, int)) for p in pos) if not (is_2D & is_real): raise TypeError( f"In node {name} the `pos` attribute must be a 2D tuple/list" f" of real numbers, got {pos} instead." ) node_weights = nx.get_node_attributes(g, "weight") if node_weights: if len(node_weights) != num_nodes: raise ValueError("Node attribute `weight` must be defined for all nodes") for name, weight in node_weights.items(): if not isinstance(weight, (float, int)): raise TypeError( f"In node {name} the `weight` attribute must be a real number, " f"got {type(weight)} instead." "" )
# validate edge attributes for u, v, data in g.edges.data(): unexpected_keys = set(data) - {"weight"} if unexpected_keys: raise ValueError(f"{unexpected_keys} not allowed in edge attributes.") edge_weights = nx.get_edge_attributes(g, "weight") if edge_weights: if len(edge_weights) != num_edges: raise ValueError("Edge attribute `weight` must be defined for all edges") for name, weight in edge_weights.items(): if not isinstance(weight, (float, int)): raise TypeError( f"In edge {name}, the attribute `weight` must be a real number, " f"got {type(weight)} instead." )
# build the instance of the graph graph = cls() graph.add_nodes_from(g.nodes) graph.add_edges_from(g.edges) graph._node_weights = nx.get_node_attributes(g, "weight", default=None) graph._coords = nx.get_node_attributes(g, "pos", default=None) graph._edge_weights = nx.get_edge_attributes(g, "weight", default=None)
return graph
interactions() -> dict
Section titled “
interactions() -> dict
”Rydberg model interaction 1/r^6 between pair of nodes.
Source code in qoolqit/graphs/base_graph.py
def interactions(self) -> dict: """Rydberg model interaction 1/r^6 between pair of nodes.""" return {p: 1.0 / (r**6) for p, r in self.distances().items()}
is_ud_graph() -> bool
Section titled “
is_ud_graph() -> bool
”Check if the graph is unit-disk.
Source code in qoolqit/graphs/base_graph.py
def is_ud_graph(self) -> bool: """Check if the graph is unit-disk.""" try: self.ud_radius_range() return True except ValueError: return False
max_distance(connected: bool | None = None) -> float
Section titled “
max_distance(connected: bool | None = None) -> float
”Returns the maximum distance in the graph.
Parameters:
connected
Section titled “ connected
”bool | None, default:None)
–if True/False, computes only over connected/disconnected nodes.
Source code in qoolqit/graphs/base_graph.py
def max_distance(self, connected: bool | None = None) -> float: """Returns the maximum distance in the graph.
Arguments: connected: if True/False, computes only over connected/disconnected nodes. """ distance: float if connected is None: distance = max(self.distances(self.all_node_pairs).values()) elif connected: distance = max(self.distances(self.sorted_edges).values()) else: distance = max(self.distances(self.all_node_pairs - self.sorted_edges).values()) return distance
min_distance(connected: bool | None = None) -> float
Section titled “
min_distance(connected: bool | None = None) -> float
”Returns the minimum distance in the graph.
Parameters:
connected
Section titled “ connected
”bool | None, default:None)
–if True/False, computes only over connected/disconnected nodes.
Source code in qoolqit/graphs/base_graph.py
def min_distance(self, connected: bool | None = None) -> float: """Returns the minimum distance in the graph.
Arguments: connected: if True/False, computes only over connected/disconnected nodes. """ distance: float if connected is None: distance = min(self.distances(self.all_node_pairs).values()) elif connected: distance = min(self.distances(self.sorted_edges).values()) else: distance = min(self.distances(self.all_node_pairs - self.sorted_edges).values()) return distance
rescale_coords(*args: Any, scaling: float | None = None, spacing: float | None = None) -> None
Section titled “
rescale_coords(*args: Any, scaling: float | None = None, spacing: float | None = None) -> None
”Rescales the node coordinates by a factor.
Accepts either a scaling or a spacing factor.
Parameters:
scaling
Section titled “ scaling
”float | None, default:None)
–value to scale by.
spacing
Section titled “ spacing
”float | None, default:None)
–value to set as the minimum distance in the graph.
Source code in qoolqit/graphs/base_graph.py
def rescale_coords( self, *args: Any, scaling: float | None = None, spacing: float | None = None,) -> None: """Rescales the node coordinates by a factor.
Accepts either a scaling or a spacing factor.
Arguments: scaling: value to scale by. spacing: value to set as the minimum distance in the graph. """ if self.has_coords: msg = "Please pass either a `scaling` or a `spacing` value as a keyword argument." if (len(args) > 0) or (scaling is None and spacing is None): raise TypeError(msg) if scaling is None and spacing is not None: self._coords = space_coords(self._coords, spacing) elif spacing is None and scaling is not None: self._coords = scale_coords(self._coords, scaling) else: raise TypeError(msg) else: raise AttributeError("Trying to rescale coordinates on a graph without coordinates.")
set_ud_edges(radius: float) -> None
Section titled “
set_ud_edges(radius: float) -> None
”Reset the set of edges to be equal to the set of unit-disk edges.
Parameters:
radius
Section titled “ radius
”float)
–the radius to use in determining the set of unit-disk edges.
Source code in qoolqit/graphs/base_graph.py
def set_ud_edges(self, radius: float) -> None: """Reset the set of edges to be equal to the set of unit-disk edges.
Arguments: radius: the radius to use in determining the set of unit-disk edges. """ self.remove_edges_from(list(self.edges)) self.add_edges_from(self.ud_edges(radius))
ud_edges(radius: float) -> set
Section titled “
ud_edges(radius: float) -> set
”Returns the set of edges given by the intersection of circles of a given radius.
Parameters:
radius
Section titled “ radius
”float)
–the value
Source code in qoolqit/graphs/base_graph.py
def ud_edges(self, radius: float) -> set: """Returns the set of edges given by the intersection of circles of a given radius.
Arguments: radius: the value """ if self.has_coords: return set(e for e, d in self.distances().items() if less_or_equal(d, radius)) else: raise AttributeError("Getting unit disk edges is not valid without coordinates.")
ud_radius_range() -> tuple
Section titled “
ud_radius_range() -> tuple
”Return the range (R_min, R_max) where the graph is unit-disk.
The graph is unit-disk if the maximum distance between all connected nodes is smaller than the minimum distance between disconnected nodes. This means that for any value R in that interval, the following condition is true:
graph.ud_edges(radius = R) == graph.sorted edges
Source code in qoolqit/graphs/base_graph.py
def ud_radius_range(self) -> tuple: """Return the range (R_min, R_max) where the graph is unit-disk.
The graph is unit-disk if the maximum distance between all connected nodes is smaller than the minimum distance between disconnected nodes. This means that for any value R in that interval, the following condition is true:
graph.ud_edges(radius = R) == graph.sorted edges """ if self.has_coords: n_edges = len(self.sorted_edges) if n_edges == 0: # If the graph is empty and has coordinates return (0.0, self.min_distance(connected=False)) elif n_edges == len(self.all_node_pairs): # If the graph is fully connected return (self.max_distance(connected=True), float("inf")) elif self.max_distance(connected=True) < self.min_distance(connected=False): return (self.max_distance(connected=True), self.min_distance(connected=False)) else: raise ValueError("Graph is not unit disk.") else: raise AttributeError("Checking if graph is unit disk is not valid without coordinates.")
DataGraph(edges: Iterable = [])
Section titled “
DataGraph(edges: Iterable = [])
”The main graph structure to represent problem data.
Parameters:
edges
Section titled “ edges
”Iterable, default:[])
–set of edge tuples (i, j)
- API reference
Methods:
-
circle–Constructs a circle graph, with the respective coordinates.
-
distances–Returns a dictionary of distances for a given set of edges.
-
draw–Draw the graph.
-
from_coordinates–Construct a base graph from a set of coordinates.
-
from_matrix–Constructs a graph from a symmetric square matrix.
-
from_nodes–Construct a base graph from a set of nodes.
-
from_nx–Convert a NetworkX Graph object into a QoolQit graph instance.
-
from_pyg–Convert a PyTorch Geometric Data object into a DataGraph instance.
-
heavy_hexagonal–Constructs a heavy-hexagonal lattice graph, with respective coordinates.
-
hexagonal–Constructs a hexagonal lattice graph, with respective coordinates.
-
interactions–Rydberg model interaction 1/r^6 between pair of nodes.
-
is_ud_graph–Check if the graph is unit-disk.
-
line–Constructs a line graph, with the respective coordinates.
-
max_distance–Returns the maximum distance in the graph.
-
min_distance–Returns the minimum distance in the graph.
-
random_er–Constructs an Erdős–Rényi random graph.
-
random_ud–Constructs a random unit-disk graph.
-
rescale_coords–Rescales the node coordinates by a factor.
-
set_ud_edges–Reset the set of edges to be equal to the set of unit-disk edges.
-
square–Constructs a square lattice graph, with respective coordinates.
-
to_pyg–Convert the DataGraph to a PyTorch Geometric Data object.
-
triangular–Constructs a triangular lattice graph, with respective coordinates.
-
ud_edges–Returns the set of edges given by the intersection of circles of a given radius.
-
ud_radius_range–Return the range (R_min, R_max) where the graph is unit-disk.
Attributes:
-
all_node_pairs(set) –Return a list of all possible node pairs in the graph.
-
coords(dict) –Return the dictionary of node coordinates.
-
edge_weights(dict) –Return the dictionary of edge weights.
-
has_coords(bool) –Check if the graph has coordinates.
-
has_edge_weights(bool) –Check if the graph has edge weights.
-
has_edges(bool) –Check if the graph has edges.
-
has_node_weights(bool) –Check if the graph has node weights.
-
node_weights(dict) –Return the dictionary of node weights.
-
sorted_edges(set) –Returns the set of edges (u, v) such that (u < v).
Source code in qoolqit/graphs/data_graph.py
def __init__(self, edges: Iterable = []) -> None: """ Default constructor for the BaseGraph.
Arguments: edges: set of edge tuples (i, j) """ super().__init__(edges)
all_node_pairs: set
property
Section titled “
all_node_pairs: set
property
”Return a list of all possible node pairs in the graph.
coords: dict
property
writable
Section titled “
coords: dict
property
writable
”Return the dictionary of node coordinates.
edge_weights: dict
property
writable
Section titled “
edge_weights: dict
property
writable
”Return the dictionary of edge weights.
has_coords: bool
property
Section titled “
has_coords: bool
property
”Check if the graph has coordinates.
Requires all nodes to have coordinates.
has_edge_weights: bool
property
Section titled “
has_edge_weights: bool
property
”Check if the graph has edge weights.
Requires all edges to have a weight.
has_edges: bool
property
Section titled “
has_edges: bool
property
”Check if the graph has edges.
has_node_weights: bool
property
Section titled “
has_node_weights: bool
property
”Check if the graph has node weights.
Requires all nodes to have a weight.
node_weights: dict
property
writable
Section titled “
node_weights: dict
property
writable
”Return the dictionary of node weights.
sorted_edges: set
property
Section titled “
sorted_edges: set
property
”Returns the set of edges (u, v) such that (u < v).
circle(n: int, spacing: float = 1.0, center: tuple = (0.0, 0.0)) -> DataGraph
classmethod
Section titled “
circle(n: int, spacing: float = 1.0, center: tuple = (0.0, 0.0)) -> DataGraph
classmethod
”Constructs a circle graph, with the respective coordinates.
Parameters:
(int)
–number of nodes.
spacing
Section titled “ spacing
”float, default:1.0)
–distance between each node.
center
Section titled “ center
”tuple, default:(0.0, 0.0))
–point (x, y) to set as the center of the graph.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef circle( cls, n: int, spacing: float = 1.0, center: tuple = (0.0, 0.0),) -> DataGraph: """Constructs a circle graph, with the respective coordinates.
Arguments: n: number of nodes. spacing: distance between each node. center: point (x, y) to set as the center of the graph. """
d_theta = (2.0 * np.pi) / n r = spacing / (2.0 * np.sin(np.pi / n)) theta = np.linspace(0.0, 2.0 * np.pi - d_theta, n) coords = [ (x + center[0], y + center[1]) for x, y in zip(r * np.cos(theta), r * np.sin(theta)) ] edges = [(i, i + 1) for i in range(n - 1)] + [(n - 1, 0)] graph = cls.from_coordinates(coords) graph.add_edges_from(edges) graph._reset_dicts() return graph
distances(edge_list: Iterable | None = None) -> dict
Section titled “
distances(edge_list: Iterable | None = None) -> dict
”Returns a dictionary of distances for a given set of edges.
Distances are calculated directly from the coordinates. Raises an error if there are no coordinates on the graph.
Parameters:
edge_list
Section titled “ edge_list
”Iterable | None, default:None)
–set of edges.
Source code in qoolqit/graphs/base_graph.py
def distances(self, edge_list: Iterable | None = None) -> dict: """Returns a dictionary of distances for a given set of edges.
Distances are calculated directly from the coordinates. Raises an error if there are no coordinates on the graph.
Arguments: edge_list: set of edges. """ if self.has_coords: if edge_list is None: edge_list = self.all_node_pairs elif len(edge_list) == 0: # type: ignore [arg-type] raise ValueError("Trying to compute distances for an empty edge list.") return distances(self.coords, edge_list) else: raise AttributeError("Trying to compute distances for a graph without coordinates.")
draw(ax: Axes | None = None, **kwargs: Any) -> None
Section titled “
draw(ax: Axes | None = None, **kwargs: Any) -> None
”Draw the graph.
Uses the draw_networkx function from NetworkX.
Parameters:
(Axes | None, default:None)
–Axes object to draw on. If None, uses the current Axes.
**kwargs
Section titled “ **kwargs
”Any, default:{})
–keyword-arguments to pass to draw_networkx.
Source code in qoolqit/graphs/base_graph.py
def draw(self, ax: Axes | None = None, **kwargs: Any) -> None: """Draw the graph.
Uses the draw_networkx function from NetworkX.
Args: ax: Axes object to draw on. If None, uses the current Axes. **kwargs: keyword-arguments to pass to draw_networkx. """ if self.has_coords: if "hide_ticks" not in kwargs: kwargs["hide_ticks"] = False
nx.draw_networkx(self, pos=self.coords, ax=ax, **kwargs)
if ax is None: ax = plt.gca() ax.set_xlabel("x") ax.set_ylabel("y") ax.grid(True, color="lightgray", linestyle="--", linewidth=0.7)
# minimum ybox ylim = ax.get_ylim() if (ylim[1] - ylim[0]) < 2: y_center = (ylim[0] + ylim[1]) / 2 ax.set_ylim(y_center - 1, y_center + 1) plt.tight_layout() else: nx.draw_networkx(self, ax=ax, **kwargs)
from_coordinates(coords: list | dict) -> BaseGraph
classmethod
Section titled “
from_coordinates(coords: list | dict) -> BaseGraph
classmethod
”Construct a base graph from a set of coordinates.
Parameters:
coords
Section titled “ coords
”list | dict)
–list or dictionary of coordinate pairs.
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_coordinates(cls, coords: list | dict) -> BaseGraph: """Construct a base graph from a set of coordinates.
Arguments: coords: list or dictionary of coordinate pairs. """ if isinstance(coords, list): nodes = list(range(len(coords))) coords_dict = {i: pos for i, pos in enumerate(coords)} elif isinstance(coords, dict): nodes = list(coords.keys()) coords_dict = coords graph = cls.from_nodes(nodes) graph._coords = coords_dict graph._reset_dicts() return graph
from_matrix(data: ArrayLike) -> DataGraph
classmethod
Section titled “
from_matrix(data: ArrayLike) -> DataGraph
classmethod
”Constructs a graph from a symmetric square matrix.
The diagonal values are set as the node weights. For each entry (i, j) where M[i, j] != 0 an edge (i, j) is added to the graph and the value M[i, j] is set as its weight.
Parameters:
(ArrayLike)
–symmetric square matrix.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef from_matrix(cls, data: ArrayLike) -> DataGraph: """Constructs a graph from a symmetric square matrix.
The diagonal values are set as the node weights. For each entry (i, j) where M[i, j] != 0 an edge (i, j) is added to the graph and the value M[i, j] is set as its weight.
Arguments: data: symmetric square matrix. """ if data.ndim != 2: raise ValueError("2D Matrix required.") if not np.allclose(data, data.T, rtol=0.0, atol=ATOL_32): raise ValueError("Matrix must be symmetric.")
diag = np.diag(data) n_nodes = len(diag) node_weights = {i: diag[i] for i in range(n_nodes)} if np.allclose(diag, np.zeros(n_nodes), rtol=0.0, atol=ATOL_32): node_weights = {i: None for i in range(n_nodes)} else: node_weights = {i: diag[i].item() for i in range(n_nodes)}
data[data <= ATOL_32] = 0.0 non_zero = data.nonzero() i_list = non_zero[0].tolist() j_list = non_zero[1].tolist()
edge_list = [(i, j) for i, j in zip(i_list, j_list) if i < j] edge_weights = {(i, j): data[i, j].item() for i, j in edge_list}
graph = cls.from_nodes(range(n_nodes)) graph.add_edges_from(edge_list) graph.node_weights = node_weights graph.edge_weights = edge_weights return graph
from_nodes(nodes: Iterable) -> BaseGraph
classmethod
Section titled “
from_nodes(nodes: Iterable) -> BaseGraph
classmethod
”Construct a base graph from a set of nodes.
Parameters:
nodes
Section titled “ nodes
”Iterable)
–set of nodes.
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_nodes(cls, nodes: Iterable) -> BaseGraph: """Construct a base graph from a set of nodes.
Arguments: nodes: set of nodes. """ graph = cls() graph.add_nodes_from(nodes) graph._coords = {i: None for i in graph.nodes} graph._reset_dicts() return graph
from_nx(g: nx.Graph) -> BaseGraph
classmethod
Section titled “
from_nx(g: nx.Graph) -> BaseGraph
classmethod
”Convert a NetworkX Graph object into a QoolQit graph instance.
The input networkx.Graph graph must be defined only with the following allowed
Node attributes
Edge attributes: weight: represents the edge weight. Must be a real number.
Returns an instance of the class with following attributes
Source code in qoolqit/graphs/base_graph.py
@classmethoddef from_nx(cls, g: nx.Graph) -> BaseGraph: """Convert a NetworkX Graph object into a QoolQit graph instance.
The input `networkx.Graph` graph must be defined only with the following allowed
Node attributes: pos (tuple): represents the node 2D position. Must be a list/tuple of real numbers. weight: represents the node weight. Must be a real number. Edge attributes: weight: represents the edge weight. Must be a real number.
Returns an instance of the class with following attributes: - _node_weights : dict[node, float or None] - _edge_weights : dict[(u,v), float or None] - _coords : dict[node, (float,float) or None] """ if not isinstance(g, nx.Graph): raise TypeError("Input must be a networkx.Graph instance.")
g = nx.convert_node_labels_to_integers(g) num_nodes = len(g.nodes) num_edges = len(g.edges)
# validate node attributes for name, data in g.nodes.data(): unexpected_keys = set(data) - {"weight", "pos"} if unexpected_keys: raise ValueError(f"{unexpected_keys} not allowed in node attributes.")
node_pos = nx.get_node_attributes(g, "pos") if node_pos: if len(node_pos) != num_nodes: raise ValueError("Node attribute `pos` must be defined for all nodes") for name, pos in node_pos.items(): is_2D = isinstance(pos, (tuple, list)) & (len(pos) == 2) is_real = all(isinstance(p, (float, int)) for p in pos) if not (is_2D & is_real): raise TypeError( f"In node {name} the `pos` attribute must be a 2D tuple/list" f" of real numbers, got {pos} instead." ) node_weights = nx.get_node_attributes(g, "weight") if node_weights: if len(node_weights) != num_nodes: raise ValueError("Node attribute `weight` must be defined for all nodes") for name, weight in node_weights.items(): if not isinstance(weight, (float, int)): raise TypeError( f"In node {name} the `weight` attribute must be a real number, " f"got {type(weight)} instead." "" )
# validate edge attributes for u, v, data in g.edges.data(): unexpected_keys = set(data) - {"weight"} if unexpected_keys: raise ValueError(f"{unexpected_keys} not allowed in edge attributes.") edge_weights = nx.get_edge_attributes(g, "weight") if edge_weights: if len(edge_weights) != num_edges: raise ValueError("Edge attribute `weight` must be defined for all edges") for name, weight in edge_weights.items(): if not isinstance(weight, (float, int)): raise TypeError( f"In edge {name}, the attribute `weight` must be a real number, " f"got {type(weight)} instead." )
# build the instance of the graph graph = cls() graph.add_nodes_from(g.nodes) graph.add_edges_from(g.edges) graph._node_weights = nx.get_node_attributes(g, "weight", default=None) graph._coords = nx.get_node_attributes(g, "pos", default=None) graph._edge_weights = nx.get_edge_attributes(g, "weight", default=None)
return graph
from_pyg(data: torch_geometric.data.Data, node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str | None = None, edge_weights_attr: str | None = None) -> DataGraph
classmethod
Section titled “
from_pyg(data: torch_geometric.data.Data, node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str | None = None, edge_weights_attr: str | None = None) -> DataGraph
classmethod
”Convert a PyTorch Geometric Data object into a DataGraph instance.
Requires torch_geometric. Uses to_networkx internally.
Default attributes copied (if present on data ):
- Node:
x,pos(posis also stored in_coords) - Edge:
edge_attr - Graph:
y
Use node_attrs, edge_attrs, graph_attrs for extras.
QoolQit weights (_node_weights, _edge_weights) are not
populated automatically — use the explicit parameters:
node_weights_attr: real-valued tensor of shape(N,)or(N, 1). Defaults toNone.edge_weights_attr: real-valued tensor of shape(E,)or(E, 1)whereE = edge_index.shape[1](directed count). Defaults toNone.
The weight attribute is also stored as a regular node/edge attribute.
Parameters:
(Data)
–PyTorch Geometric Data object to convert.
node_attrs
Section titled “ node_attrs
”Iterable[str] | None, default:None)
–extra node attributes to copy (beyond x and pos).
edge_attrs
Section titled “ edge_attrs
”Iterable[str] | None, default:None)
–extra edge attributes to copy (beyond edge_attr).
graph_attrs
Section titled “ graph_attrs
”Iterable[str] | None, default:None)
–extra graph-level attributes to copy (beyond y).
node_weights_attr
Section titled “ node_weights_attr
”str | None, default:None)
–Data attribute to use as node weights.
edge_weights_attr
Section titled “ edge_weights_attr
”str | None, default:None)
–Data attribute to use as edge weights.
Returns:
-
DataGraph–DataGraph with
_coords,_node_weights,_edge_weights -
DataGraph–populated where applicable.
Raises:
-
ImportError–if
torch_geometricis not installed. -
TypeError–if
datais not atorch_geometric.data.Datainstance, or if a weight attribute is not atorch.Tensor. -
AttributeError–if a specified weight attribute is missing.
-
ValueError–if a weight tensor has an incompatible shape or size.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef from_pyg( cls, data: torch_geometric.data.Data, node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str | None = None, edge_weights_attr: str | None = None,) -> DataGraph: """Convert a PyTorch Geometric Data object into a DataGraph instance.
Requires ``torch_geometric``. Uses ``to_networkx`` internally.
**Default attributes copied (if present on** ``data`` **):**
- Node: ``x``, ``pos`` (``pos`` is also stored in ``_coords``) - Edge: ``edge_attr`` - Graph: ``y``
Use ``node_attrs``, ``edge_attrs``, ``graph_attrs`` for extras.
**QoolQit weights** (``_node_weights``, ``_edge_weights``) are not populated automatically — use the explicit parameters:
- ``node_weights_attr``: real-valued tensor of shape ``(N,)`` or ``(N, 1)``. Defaults to ``None``. - ``edge_weights_attr``: real-valued tensor of shape ``(E,)`` or ``(E, 1)`` where ``E = edge_index.shape[1]`` (directed count). Defaults to ``None``.
The weight attribute is also stored as a regular node/edge attribute.
Arguments: data: PyTorch Geometric Data object to convert. node_attrs: extra node attributes to copy (beyond x and pos). edge_attrs: extra edge attributes to copy (beyond edge_attr). graph_attrs: extra graph-level attributes to copy (beyond y). node_weights_attr: Data attribute to use as node weights. edge_weights_attr: Data attribute to use as edge weights.
Returns: DataGraph with ``_coords``, ``_node_weights``, ``_edge_weights`` populated where applicable.
Raises: ImportError: if ``torch_geometric`` is not installed. TypeError: if ``data`` is not a ``torch_geometric.data.Data`` instance, or if a weight attribute is not a ``torch.Tensor``. AttributeError: if a specified weight attribute is missing. ValueError: if a weight tensor has an incompatible shape or size. """ try: from torch_geometric.data import Data from torch_geometric.utils import to_networkx except ImportError as e: raise ImportError("Please, install the `torch_geometric` package.") from e
if not isinstance(data, Data): raise TypeError("Input must be a torch_geometric.data.Data object.")
# Validate weight attrs early and keep the squeezed tensors node_tensor = ( cls._validate_weights_attr(data, node_weights_attr, data.num_nodes, "node") if node_weights_attr is not None else None ) edge_tensor = ( cls._validate_weights_attr(data, edge_weights_attr, data.num_edges, "edge") if edge_weights_attr is not None else None )
# Select unique attributes and add default ones only if present in the data node_attrs_set = {k for k in {"x", "pos"} if k in data} if node_attrs is not None: node_attrs_set |= set(node_attrs) if node_weights_attr is not None: node_attrs_set.add(node_weights_attr)
edge_attrs_set = {k for k in {"edge_attr"} if k in data} if edge_attrs is not None: edge_attrs_set |= set(edge_attrs) if edge_weights_attr is not None: edge_attrs_set.add(edge_weights_attr)
graph_attrs_set = {k for k in {"y"} if k in data} if graph_attrs is not None: graph_attrs_set |= set(graph_attrs)
# Convert to NetworkX (undirected, no self-loops) nx_graph = to_networkx( data, node_attrs=list(node_attrs_set), edge_attrs=list(edge_attrs_set), graph_attrs=list(graph_attrs_set), to_undirected=True, remove_self_loops=True, )
# Build the DataGraph: edges carry their data, nodes carry their data graph = cls(nx_graph.edges(data=True)) graph.add_nodes_from(nx_graph.nodes(data=True)) graph.graph = nx_graph.graph
# Re-initialize QoolQit internal dicts for all nodes/edges graph._coords = {n: None for n in graph.nodes} graph._reset_dicts()
# pos → _coords (stored as list [x, y] by to_networkx) for node, node_data in nx_graph.nodes(data=True): if "pos" in node_data: graph._coords[node] = tuple(node_data["pos"]) # type: ignore[assignment]
# node_weights_attr → _node_weights if node_tensor is not None: for i in range(data.num_nodes): graph._node_weights[i] = node_tensor[i].item()
# edge_weights_attr → _edge_weights if edge_tensor is not None: seen: set = set() for idx in range(data.edge_index.shape[1]): u = int(data.edge_index[0, idx].item()) v = int(data.edge_index[1, idx].item()) key = (min(u, v), max(u, v)) if key not in seen: graph._edge_weights[key] = edge_tensor[idx].item() seen.add(key)
return graph
heavy_hexagonal(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
Section titled “
heavy_hexagonal(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
”Constructs a heavy-hexagonal lattice graph, with respective coordinates.
Parameters:
(int)
–Number of rows of hexagons.
int)
–Number of columns of hexagons.
spacing
Section titled “ spacing
”float, default:1.0)
–The distance between adjacent nodes on the final lattice.
Notes
Source code in qoolqit/graphs/data_graph.py
@classmethoddef heavy_hexagonal( cls, m: int, n: int, spacing: float = 1.0,) -> DataGraph: """ Constructs a heavy-hexagonal lattice graph, with respective coordinates.
Arguments: m: Number of rows of hexagons. n: Number of columns of hexagons. spacing: The distance between adjacent nodes on the final lattice.
Notes: The heavy-hexagonal lattice is a regular hexagonal lattice where each edge is decorated with an additional lattice site. """ G_hex = nx.hexagonal_lattice_graph(m, n, with_positions=True) pos_unit = nx.get_node_attributes(G_hex, "pos")
G_heavy = nx.Graph() scaling_factor = 2 * spacing
label_map = {} for old_label, (x, y) in pos_unit.items(): # Relabel to an even-integer grid to make space for midpoint nodes new_label = (2 * old_label[0], 2 * old_label[1]) label_map[old_label] = new_label
# Scale positions and add the node to the new graph new_pos = (x * scaling_factor, y * scaling_factor) G_heavy.add_node(new_label, pos=new_pos)
for u_old, v_old in G_hex.edges(): u_new, v_new = label_map[u_old], label_map[v_old]
mid_label = ((u_new[0] + v_new[0]) // 2, (u_new[1] + v_new[1]) // 2)
pos_u = G_heavy.nodes[u_new]["pos"] pos_v = G_heavy.nodes[v_new]["pos"] mid_pos = ((pos_u[0] + pos_v[0]) / 2, (pos_u[1] + pos_v[1]) / 2)
G_heavy.add_node(mid_label, pos=mid_pos) G_heavy.add_edge(u_new, mid_label) G_heavy.add_edge(mid_label, v_new)
final_nodes = sorted(list(G_heavy.nodes()))
final_coords = [G_heavy.nodes[label]["pos"] for label in final_nodes]
label_to_int = {label: i for i, label in enumerate(final_nodes)}
final_edges = [(label_to_int[u], label_to_int[v]) for u, v in G_heavy.edges()]
graph = cls.from_coordinates(final_coords) graph.add_edges_from(final_edges) graph._reset_dicts() return graph
hexagonal(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
Section titled “
hexagonal(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
”Constructs a hexagonal lattice graph, with respective coordinates.
Parameters:
(int)
–Number of rows of hexagons.
int)
–Number of columns of hexagons.
spacing
Section titled “ spacing
”float, default:1.0)
–The distance between adjacent nodes on the final lattice.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef hexagonal( cls, m: int, n: int, spacing: float = 1.0,) -> DataGraph: """ Constructs a hexagonal lattice graph, with respective coordinates.
Arguments: m: Number of rows of hexagons. n: Number of columns of hexagons. spacing: The distance between adjacent nodes on the final lattice. """ G = nx.hexagonal_lattice_graph(m, n, with_positions=True) G = nx.convert_node_labels_to_integers(G) pos_unit = nx.get_node_attributes(G, "pos")
final_pos = {node: (x * spacing, y * spacing) for node, (x, y) in pos_unit.items()}
graph = cls.from_coordinates(final_pos) graph.add_edges_from(G.edges) graph._reset_dicts() return graph
interactions() -> dict
Section titled “
interactions() -> dict
”Rydberg model interaction 1/r^6 between pair of nodes.
Source code in qoolqit/graphs/base_graph.py
def interactions(self) -> dict: """Rydberg model interaction 1/r^6 between pair of nodes.""" return {p: 1.0 / (r**6) for p, r in self.distances().items()}
is_ud_graph() -> bool
Section titled “
is_ud_graph() -> bool
”Check if the graph is unit-disk.
Source code in qoolqit/graphs/base_graph.py
def is_ud_graph(self) -> bool: """Check if the graph is unit-disk.""" try: self.ud_radius_range() return True except ValueError: return False
line(n: int, spacing: float = 1.0) -> DataGraph
classmethod
Section titled “
line(n: int, spacing: float = 1.0) -> DataGraph
classmethod
”Constructs a line graph, with the respective coordinates.
Parameters:
(int)
–number of nodes.
spacing
Section titled “ spacing
”float, default:1.0)
–distance between each node.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef line(cls, n: int, spacing: float = 1.0) -> DataGraph: """Constructs a line graph, with the respective coordinates.
Arguments: n: number of nodes. spacing: distance between each node. """ coords = [(i * spacing, 0.0) for i in range(n)] graph = cls.from_coordinates(coords) edges = [(i, i + 1) for i in range(0, n - 1)] graph.add_edges_from(edges) graph._reset_dicts() return graph
max_distance(connected: bool | None = None) -> float
Section titled “
max_distance(connected: bool | None = None) -> float
”Returns the maximum distance in the graph.
Parameters:
connected
Section titled “ connected
”bool | None, default:None)
–if True/False, computes only over connected/disconnected nodes.
Source code in qoolqit/graphs/base_graph.py
def max_distance(self, connected: bool | None = None) -> float: """Returns the maximum distance in the graph.
Arguments: connected: if True/False, computes only over connected/disconnected nodes. """ distance: float if connected is None: distance = max(self.distances(self.all_node_pairs).values()) elif connected: distance = max(self.distances(self.sorted_edges).values()) else: distance = max(self.distances(self.all_node_pairs - self.sorted_edges).values()) return distance
min_distance(connected: bool | None = None) -> float
Section titled “
min_distance(connected: bool | None = None) -> float
”Returns the minimum distance in the graph.
Parameters:
connected
Section titled “ connected
”bool | None, default:None)
–if True/False, computes only over connected/disconnected nodes.
Source code in qoolqit/graphs/base_graph.py
def min_distance(self, connected: bool | None = None) -> float: """Returns the minimum distance in the graph.
Arguments: connected: if True/False, computes only over connected/disconnected nodes. """ distance: float if connected is None: distance = min(self.distances(self.all_node_pairs).values()) elif connected: distance = min(self.distances(self.sorted_edges).values()) else: distance = min(self.distances(self.all_node_pairs - self.sorted_edges).values()) return distance
random_er(n: int, p: float, seed: int | None = None) -> DataGraph
classmethod
Section titled “
random_er(n: int, p: float, seed: int | None = None) -> DataGraph
classmethod
”Constructs an Erdős–Rényi random graph.
Parameters:
(int)
–number of nodes.
float)
–probability that any two nodes connect.
int | None, default:None)
–random seed.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef random_er(cls, n: int, p: float, seed: int | None = None) -> DataGraph: """Constructs an Erdős–Rényi random graph.
Arguments: n: number of nodes. p: probability that any two nodes connect. seed: random seed. """ base_graph = nx.erdos_renyi_graph(n, p, seed) graph = DataGraph.from_nodes(list(base_graph.nodes)) graph.add_edges_from(base_graph.edges) graph._reset_dicts() return graph
random_ud(n: int, radius: float = 1.0, L: float | None = None) -> DataGraph
classmethod
Section titled “
random_ud(n: int, radius: float = 1.0, L: float | None = None) -> DataGraph
classmethod
”Constructs a random unit-disk graph.
The nodes are sampled uniformly from a square of size (L x L). If L is not given, it is estimated based on a rough heuristic that of packing N nodes on a square of side L such that the expected minimum distance is R, leading to L ~ (R / 2) * sqrt(π * n).
Parameters:
(int)
–number of nodes.
radius
Section titled “ radius
”float, default:1.0)
–radius to use for defining the unit-disk edges.
float | None, default:None)
–size of the square on which to sample the node coordinates.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef random_ud( cls, n: int, radius: float = 1.0, L: float | None = None,) -> DataGraph: """Constructs a random unit-disk graph.
The nodes are sampled uniformly from a square of size (L x L). If L is not given, it is estimated based on a rough heuristic that of packing N nodes on a square of side L such that the expected minimum distance is R, leading to L ~ (R / 2) * sqrt(π * n).
Arguments: n: number of nodes. radius: radius to use for defining the unit-disk edges. L: size of the square on which to sample the node coordinates. """ if L is None: L = (radius / 2) * ((np.pi * n) ** 0.5) coords = random_coords(n, L) graph = cls.from_coordinates(coords) edges = graph.ud_edges(radius) graph.add_edges_from(edges) graph._reset_dicts() return graph
rescale_coords(*args: Any, scaling: float | None = None, spacing: float | None = None) -> None
Section titled “
rescale_coords(*args: Any, scaling: float | None = None, spacing: float | None = None) -> None
”Rescales the node coordinates by a factor.
Accepts either a scaling or a spacing factor.
Parameters:
scaling
Section titled “ scaling
”float | None, default:None)
–value to scale by.
spacing
Section titled “ spacing
”float | None, default:None)
–value to set as the minimum distance in the graph.
Source code in qoolqit/graphs/base_graph.py
def rescale_coords( self, *args: Any, scaling: float | None = None, spacing: float | None = None,) -> None: """Rescales the node coordinates by a factor.
Accepts either a scaling or a spacing factor.
Arguments: scaling: value to scale by. spacing: value to set as the minimum distance in the graph. """ if self.has_coords: msg = "Please pass either a `scaling` or a `spacing` value as a keyword argument." if (len(args) > 0) or (scaling is None and spacing is None): raise TypeError(msg) if scaling is None and spacing is not None: self._coords = space_coords(self._coords, spacing) elif spacing is None and scaling is not None: self._coords = scale_coords(self._coords, scaling) else: raise TypeError(msg) else: raise AttributeError("Trying to rescale coordinates on a graph without coordinates.")
set_ud_edges(radius: float) -> None
Section titled “
set_ud_edges(radius: float) -> None
”Reset the set of edges to be equal to the set of unit-disk edges.
Source code in qoolqit/graphs/data_graph.py
def set_ud_edges(self, radius: float) -> None: """Reset the set of edges to be equal to the set of unit-disk edges.""" super().set_ud_edges(radius=radius) self._edge_weights = {e: None for e in self.sorted_edges}
square(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
Section titled “
square(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
”Constructs a square lattice graph, with respective coordinates.
Parameters:
(int)
–Number of rows of square.
int)
–Number of columns of square.
spacing
Section titled “ spacing
”float, default:1.0)
–The distance between adjacent nodes on the final lattice.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef square( cls, m: int, n: int, spacing: float = 1.0,) -> DataGraph: """ Constructs a square lattice graph, with respective coordinates.
Arguments: m: Number of rows of square. n: Number of columns of square. spacing: The distance between adjacent nodes on the final lattice. """ G = nx.grid_2d_graph(m, n) final_coords = [(x * spacing, y * spacing) for (x, y) in list(G.nodes)] G = nx.convert_node_labels_to_integers(G)
graph = DataGraph.from_coordinates(final_coords) graph.add_edges_from(G.edges) graph._reset_dicts() return graph
to_pyg(node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str = 'weight', edge_weights_attr: str = 'edge_weight') -> torch_geometric.data.Data
Section titled “
to_pyg(node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str = 'weight', edge_weights_attr: str = 'edge_weight') -> torch_geometric.data.Data
”Convert the DataGraph to a PyTorch Geometric Data object.
Requires torch_geometric. Uses from_networkx internally.
Default attributes exported (if present on the graph):
- Node
"x"→data.x; Edge"edge_attr"→data.edge_attr - Graph
"y"→data.y
Use node_attrs, edge_attrs, graph_attrs for extras.
QoolQit internal dicts exported when populated:
_coords→data.pos(float64, shape(N, 2))_node_weights→data.<node_weights_attr>(float64, shape(N,)). Defaults to"weight"._edge_weights→data.<edge_weights_attr>(float64, shape(2*E,)). Defaults to"edge_weight".
Parameters:
node_attrs
Section titled “ node_attrs
”Iterable[str] | None, default:None)
–extra node attributes to export (beyond x).
edge_attrs
Section titled “ edge_attrs
”Iterable[str] | None, default:None)
–extra edge attributes to export (beyond edge_attr).
graph_attrs
Section titled “ graph_attrs
”Iterable[str] | None, default:None)
–extra graph-level attributes to export (beyond y).
node_weights_attr
Section titled “ node_weights_attr
”str, default:'weight')
–Data attribute name for node weights.
Defaults to "weight".
edge_weights_attr
Section titled “ edge_weights_attr
”str, default:'edge_weight')
–Data attribute name for edge weights.
Defaults to "edge_weight".
Returns:
-
Data–PyTorch Geometric Data object.
Raises:
-
ImportError–if
torch_geometricis not installed.
Source code in qoolqit/graphs/data_graph.py
def to_pyg( self, node_attrs: Iterable[str] | None = None, edge_attrs: Iterable[str] | None = None, graph_attrs: Iterable[str] | None = None, node_weights_attr: str = "weight", edge_weights_attr: str = "edge_weight",) -> torch_geometric.data.Data: """Convert the DataGraph to a PyTorch Geometric Data object.
Requires ``torch_geometric``. Uses ``from_networkx`` internally.
**Default attributes exported (if present on the graph):**
- Node ``"x"`` → ``data.x``; Edge ``"edge_attr"`` → ``data.edge_attr`` - Graph ``"y"`` → ``data.y``
Use ``node_attrs``, ``edge_attrs``, ``graph_attrs`` for extras.
**QoolQit internal dicts exported when populated:**
- ``_coords`` → ``data.pos`` (float64, shape ``(N, 2)``) - ``_node_weights`` → ``data.`` (float64, shape ``(N,)``). Defaults to ``"weight"``. - ``_edge_weights`` → ``data.`` (float64, shape ``(2*E,)``). Defaults to ``"edge_weight"``.
Arguments: node_attrs: extra node attributes to export (beyond x). edge_attrs: extra edge attributes to export (beyond edge_attr). graph_attrs: extra graph-level attributes to export (beyond y). node_weights_attr: Data attribute name for node weights. Defaults to ``"weight"``. edge_weights_attr: Data attribute name for edge weights. Defaults to ``"edge_weight"``.
Returns: PyTorch Geometric Data object.
Raises: ImportError: if ``torch_geometric`` is not installed. """ try: import torch from torch_geometric.utils import from_networkx except ImportError as e: raise ImportError("Please, install the `torch_geometric` package.") from e
node_attrs_set = set(node_attrs) if node_attrs else set() edge_attrs_set = set(edge_attrs) if edge_attrs else set() graph_attrs_list = list(graph_attrs) if graph_attrs else []
# Add default PyG attributes if present in the graph if any("x" in d for _, d in self.nodes(data=True)): node_attrs_set.add("x") if any("edge_attr" in d for _, _, d in self.edges(data=True)): edge_attrs_set.add("edge_attr") if "y" in self.graph: graph_attrs_list.append("y")
# Build a filtered copy with only the requested attributes filtered_graph = nx.Graph() filtered_graph.add_nodes_from(self.nodes()) filtered_graph.add_edges_from(self.edges())
for node, node_data in self.nodes(data=True): for key, value in node_data.items(): if key in node_attrs_set: filtered_graph.nodes[node][key] = value for u, v, edge_data in self.edges(data=True): for key, value in edge_data.items(): if key in edge_attrs_set: filtered_graph.edges[u, v][key] = value for attr in graph_attrs_list: if attr in self.graph: filtered_graph.graph[attr] = self.graph[attr]
data = from_networkx(filtered_graph)
# Export _coords → pos if self.has_coords: positions = [self._coords[n] for n in sorted(self.nodes())] data.pos = torch.tensor(positions, dtype=torch.float64)
# Export _node_weights → node_weights_attr if self.has_node_weights: weights = [self._node_weights[n] for n in sorted(self.nodes())] setattr(data, node_weights_attr, torch.tensor(weights, dtype=torch.float64))
# Export _edge_weights → edge_weights_attr (one value per directed edge in edge_index) if self.has_edge_weights: edge_weights: list[float] = [] for i in range(data.edge_index.shape[1]): u, v = int(data.edge_index[0, i].item()), int(data.edge_index[1, i].item()) edge_key = (min(u, v), max(u, v)) edge_weights.append(float(self._edge_weights[edge_key])) # type: ignore[arg-type] setattr(data, edge_weights_attr, torch.tensor(edge_weights, dtype=torch.float64))
return data
triangular(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
Section titled “
triangular(m: int, n: int, spacing: float = 1.0) -> DataGraph
classmethod
”Constructs a triangular lattice graph, with respective coordinates.
Parameters:
(int)
–Number of rows of triangles.
int)
–Number of columns of triangles.
spacing
Section titled “ spacing
”float, default:1.0)
–The distance between adjacent nodes on the final lattice.
Source code in qoolqit/graphs/data_graph.py
@classmethoddef triangular( cls, m: int, n: int, spacing: float = 1.0,) -> DataGraph: """ Constructs a triangular lattice graph, with respective coordinates.
Arguments: m: Number of rows of triangles. n: Number of columns of triangles. spacing: The distance between adjacent nodes on the final lattice. """ G = nx.triangular_lattice_graph(m, n, with_positions=True) G = nx.convert_node_labels_to_integers(G) pos_unit = nx.get_node_attributes(G, "pos")
final_pos = {node: (x * spacing, y * spacing) for node, (x, y) in pos_unit.items()}
graph = cls.from_coordinates(final_pos) graph.add_edges_from(G.edges) graph._reset_dicts() return graph
ud_edges(radius: float) -> set
Section titled “
ud_edges(radius: float) -> set
”Returns the set of edges given by the intersection of circles of a given radius.
Parameters:
radius
Section titled “ radius
”float)
–the value
Source code in qoolqit/graphs/base_graph.py
def ud_edges(self, radius: float) -> set: """Returns the set of edges given by the intersection of circles of a given radius.
Arguments: radius: the value """ if self.has_coords: return set(e for e, d in self.distances().items() if less_or_equal(d, radius)) else: raise AttributeError("Getting unit disk edges is not valid without coordinates.")
ud_radius_range() -> tuple
Section titled “
ud_radius_range() -> tuple
”Return the range (R_min, R_max) where the graph is unit-disk.
The graph is unit-disk if the maximum distance between all connected nodes is smaller than the minimum distance between disconnected nodes. This means that for any value R in that interval, the following condition is true:
graph.ud_edges(radius = R) == graph.sorted edges
Source code in qoolqit/graphs/base_graph.py
def ud_radius_range(self) -> tuple: """Return the range (R_min, R_max) where the graph is unit-disk.
The graph is unit-disk if the maximum distance between all connected nodes is smaller than the minimum distance between disconnected nodes. This means that for any value R in that interval, the following condition is true:
graph.ud_edges(radius = R) == graph.sorted edges """ if self.has_coords: n_edges = len(self.sorted_edges) if n_edges == 0: # If the graph is empty and has coordinates return (0.0, self.min_distance(connected=False)) elif n_edges == len(self.all_node_pairs): # If the graph is fully connected return (self.max_distance(connected=True), float("inf")) elif self.max_distance(connected=True) < self.min_distance(connected=False): return (self.max_distance(connected=True), self.min_distance(connected=False)) else: raise ValueError("Graph is not unit disk.") else: raise AttributeError("Checking if graph is unit disk is not valid without coordinates.")
all_node_pairs(nodes: Iterable) -> set
Section titled “
all_node_pairs(nodes: Iterable) -> set
”Return all pairs of nodes (u, v) where u < v.
Parameters:
nodes
Section titled “ nodes
”Iterable)
–set of node indices.
Source code in qoolqit/graphs/utils.py
def all_node_pairs(nodes: Iterable) -> set: """Return all pairs of nodes (u, v) where u < v.
Arguments: nodes: set of node indices. """ return set(filter(lambda x: x[0] < x[1], product(nodes, nodes)))
distances(coords: dict, edge_list: Iterable) -> dict
Section titled “
distances(coords: dict, edge_list: Iterable) -> dict
”Return a dictionary of edge distances.
Parameters:
coords
Section titled “ coords
”dict)
–dictionary of node coordinates.
edge_list
Section titled “ edge_list
”Iterable)
–edge list to compute the distances for.
Source code in qoolqit/graphs/utils.py
def distances(coords: dict, edge_list: Iterable) -> dict: """Return a dictionary of edge distances.
Arguments: coords: dictionary of node coordinates. edge_list: edge list to compute the distances for. """ return {edge: dist(coords[edge[0]], coords[edge[1]]) for edge in edge_list}
random_coords(n: int, L: float = 1.0) -> list
Section titled “
random_coords(n: int, L: float = 1.0) -> list
”Generate a random set of node coordinates on a square of side L.
Parameters:
(int)
–number of coordinate pairs to generate.
float, default:1.0)
–side of the square.
Source code in qoolqit/graphs/utils.py
def random_coords(n: int, L: float = 1.0) -> list: """Generate a random set of node coordinates on a square of side L.
Arguments: n: number of coordinate pairs to generate. L: side of the square. """ x_coords = np.random.uniform(low=-L / 2, high=L / 2, size=(n,)).tolist() y_coords = np.random.uniform(low=-L / 2, high=L / 2, size=(n,)).tolist() return [(x, y) for x, y in zip(x_coords, y_coords)]
random_edge_list(nodes: Iterable, k: int) -> list
Section titled “
random_edge_list(nodes: Iterable, k: int) -> list
”Generates a random set of k edges linkings items from a set of nodes.
Source code in qoolqit/graphs/utils.py
def random_edge_list(nodes: Iterable, k: int) -> list: """Generates a random set of k edges linkings items from a set of nodes.""" all_edges = all_node_pairs(nodes) return random.sample(tuple(all_edges), k=k)
scale_coords(coords: dict, scaling: float) -> dict
Section titled “
scale_coords(coords: dict, scaling: float) -> dict
”Scale the coordinates by a given value.
Parameters:
coords
Section titled “ coords
”dict)
–dictionary of node coordinates.
scaling
Section titled “ scaling
”float)
–value to scale by.
Source code in qoolqit/graphs/utils.py
def scale_coords(coords: dict, scaling: float) -> dict: """Scale the coordinates by a given value.
Arguments: coords: dictionary of node coordinates. scaling: value to scale by. """ scaled_coords = {i: (c[0] * scaling, c[1] * scaling) for i, c in coords.items()} return scaled_coords
space_coords(coords: dict, spacing: float) -> dict
Section titled “
space_coords(coords: dict, spacing: float) -> dict
”Spaces the coordinates so the minimum distance is equal to a set spacing.
Parameters:
coords
Section titled “ coords
”dict)
–dictionary of node coordinates.
spacing
Section titled “ spacing
”float)
–value to set as minimum distance.
Source code in qoolqit/graphs/utils.py
def space_coords(coords: dict, spacing: float) -> dict: """Spaces the coordinates so the minimum distance is equal to a set spacing.
Arguments: coords: dictionary of node coordinates. spacing: value to set as minimum distance. """ pairs = all_node_pairs(list(coords.keys())) min_dist = min(distances(coords, pairs).values()) scale_factor = spacing / min_dist return scale_coords(coords, scale_factor)