Skip to content

Results are limited to the current section : Qoolqit

qoolqit.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.

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:

(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()

Return a list of all possible node pairs in the graph.

Return the dictionary of node coordinates.

Check if the graph has coordinates.

Requires all nodes to have coordinates.

Check if the graph has edge weights.

Requires all edges to have a weight.

Check if the graph has edges.

Check if the graph has node weights.

Requires all nodes to have a weight.

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) -&gt; 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:

(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) -&gt; 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.

(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) -&gt; BaseGraph classmethod ”

Construct a base graph from a set of coordinates.

Parameters:

(list | dict) –

list or dictionary of coordinate pairs.

Source code in qoolqit/graphs/base_graph.py
@classmethod
def 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) -&gt; BaseGraph classmethod ”

Construct a base graph from a set of nodes.

Parameters:

(Iterable) –

set of nodes.

Source code in qoolqit/graphs/base_graph.py
@classmethod
def 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

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
@classmethod
def 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

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()}

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) -&gt; float ”

Returns the maximum distance in the graph.

Parameters:

(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) -&gt; float ”

Returns the minimum distance in the graph.

Parameters:

(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) -&gt; None ”

Rescales the node coordinates by a factor.

Accepts either a scaling or a spacing factor.

Parameters:

(float | None, default:None) –

value to scale by.

(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.")

Reset the set of edges to be equal to the set of unit-disk edges.

Parameters:

(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))

Returns the set of edges given by the intersection of circles of a given radius.

Parameters:

(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.")

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.")

The main graph structure to represent problem data.

Parameters:

(Iterable, default:[]) –

set of edge tuples (i, j)

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)

Return a list of all possible node pairs in the graph.

Return the dictionary of node coordinates.

Return the dictionary of edge weights.

Check if the graph has coordinates.

Requires all nodes to have coordinates.

Check if the graph has edge weights.

Requires all edges to have a weight.

Check if the graph has edges.

Check if the graph has node weights.

Requires all nodes to have a weight.

Return the dictionary of node weights.

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)) -&gt; DataGraph classmethod ”

Constructs a circle graph, with the respective coordinates.

Parameters:

(int) –

number of nodes.

(float, default:1.0) –

distance between each node.

(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
@classmethod
def 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) -&gt; 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:

(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) -&gt; 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.

(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) -&gt; BaseGraph classmethod ”

Construct a base graph from a set of coordinates.

Parameters:

(list | dict) –

list or dictionary of coordinate pairs.

Source code in qoolqit/graphs/base_graph.py
@classmethod
def 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) -&gt; 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
@classmethod
def 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) -&gt; BaseGraph classmethod ”

Construct a base graph from a set of nodes.

Parameters:

(Iterable) –

set of nodes.

Source code in qoolqit/graphs/base_graph.py
@classmethod
def 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

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
@classmethod
def 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) -&gt; 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 (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.

Parameters:

(Data) –

PyTorch Geometric Data object to convert.

(Iterable[str] | None, default:None) –

extra node attributes to copy (beyond x and pos).

(Iterable[str] | None, default:None) –

extra edge attributes to copy (beyond edge_attr).

(Iterable[str] | None, default:None) –

extra graph-level attributes to copy (beyond y).

(str | None, default:None) –

Data attribute to use as node weights.

(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_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.

Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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) -&gt; DataGraph classmethod ”

Constructs a heavy-hexagonal lattice graph, with respective coordinates.

Parameters:

(int) –

Number of rows of hexagons.

(int) –

Number of columns of hexagons.

(float, default:1.0) –

The distance between adjacent nodes on the final lattice.

Notes
Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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) -&gt; DataGraph classmethod ”

Constructs a hexagonal lattice graph, with respective coordinates.

Parameters:

(int) –

Number of rows of hexagons.

(int) –

Number of columns of hexagons.

(float, default:1.0) –

The distance between adjacent nodes on the final lattice.

Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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

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()}

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) -&gt; DataGraph classmethod ”

Constructs a line graph, with the respective coordinates.

Parameters:

(int) –

number of nodes.

(float, default:1.0) –

distance between each node.

Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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) -&gt; float ”

Returns the maximum distance in the graph.

Parameters:

(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) -&gt; float ”

Returns the minimum distance in the graph.

Parameters:

(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) -&gt; 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
@classmethod
def 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) -&gt; 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.

(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
@classmethod
def 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) -&gt; None ”

Rescales the node coordinates by a factor.

Accepts either a scaling or a spacing factor.

Parameters:

(float | None, default:None) –

value to scale by.

(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.")

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) -&gt; DataGraph classmethod ”

Constructs a square lattice graph, with respective coordinates.

Parameters:

(int) –

Number of rows of square.

(int) –

Number of columns of square.

(float, default:1.0) –

The distance between adjacent nodes on the final lattice.

Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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 = &#39;weight&#39;, edge_weights_attr: str = &#39;edge_weight&#39;) -&gt; 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:

  • _coordsdata.pos (float64, shape (N, 2))
  • _node_weightsdata.<node_weights_attr> (float64, shape (N,)). Defaults to "weight".
  • _edge_weightsdata.<edge_weights_attr> (float64, shape (2*E,)). Defaults to "edge_weight".

Parameters:

(Iterable[str] | None, default:None) –

extra node attributes to export (beyond x).

(Iterable[str] | None, default:None) –

extra edge attributes to export (beyond edge_attr).

(Iterable[str] | None, default:None) –

extra graph-level attributes to export (beyond y).

(str, default:'weight') –

Data attribute name for node weights. Defaults to "weight".

(str, default:'edge_weight') –

Data attribute name for edge weights. Defaults to "edge_weight".

Returns:

  • Data

    PyTorch Geometric Data object.

Raises:

  • ImportError

    if torch_geometric is 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) -&gt; DataGraph classmethod ”

Constructs a triangular lattice graph, with respective coordinates.

Parameters:

(int) –

Number of rows of triangles.

(int) –

Number of columns of triangles.

(float, default:1.0) –

The distance between adjacent nodes on the final lattice.

Source code in qoolqit/graphs/data_graph.py
@classmethod
def 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

Returns the set of edges given by the intersection of circles of a given radius.

Parameters:

(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.")

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.")

Return all pairs of nodes (u, v) where u < v.

Parameters:

(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) -&gt; dict ”

Return a dictionary of edge distances.

Parameters:

(dict) –

dictionary of node coordinates.

(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}

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) -&gt; 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) -&gt; dict ”

Scale the coordinates by a given value.

Parameters:

(dict) –

dictionary of node coordinates.

(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) -&gt; dict ”

Spaces the coordinates so the minimum distance is equal to a set spacing.

Parameters:

(dict) –

dictionary of node coordinates.

(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)