Skip to content

Results are limited to the current section : Qoolqit

Graphs

In this section, you will learn how to:

  • work with DataGraph objects to represent problem data,
  • create, inspect, draw, and construct graphs in different ways,
  • use coordinates, distances, weights, and unit-disk graph properties,

Working with graphs is an essential part of computations with the Rydberg analog model. For that reason, QoolQit implements a specific DataGraph class to serve as the basis of all graph creation and manipulation, and setting the logic related to unit-disk graphs. QoolQit integrates with NetworkX (external) for many operations, and the DataGraph inherits from nx.Graph.

The DataGraph is an undirected graph with no self loops. The default way to instantiate a DataGraph is with a set of edges.

from qoolqit import DataGraph
edges = [(0, 1), (1, 2), (2, 3), (3, 0)]
graph = DataGraph(edges)
graph.nodes, graph.edges

These are the standard NodeView and EdgeView objects from NetworkX, and thus can be used add and access node and edge attributes.

We can draw the graph with graph.draw(), which calls draw_networkx (external). As such, optional arguments can be passed that will be fed to NetworkX.

import networkx as nx
pos = nx.circular_layout(graph)
graph.draw(pos=pos)

One convenient property added by QoolQit is the sorted_edges, which guarantees that the indices in each edge tuple are always provided as (u,v):u<v(u, v):u<v. This condition is not guaranteed by calling the NetworkX property graph.edges, but is sometimes useful.

graph.sorted_edges
graph.all_node_pairs
# The list must have the same length as the number of nodes:
graph.coords = [(-0.5, -0.5), (-0.5, 0.5), (0.5, 0.5), (0.5, -0.5)]
graph.coords
# Compute for all node pairs
graph.distances()
print(graph.distances())
# Compute only for connected nodes
graph.distances(graph.sorted_edges)
print(graph.distances(graph.sorted_edges))
# Compute for a specific set of node pairs
graph.distances([(0, 1), (0, 2)])
print(graph.distances([(0, 1), (0, 2)]))
graph.draw()
# Rescale coordinates by a constant factor
graph.rescale_coords(scaling = 2.0)
# Rescale coordinates by setting the minimum spacing
graph.rescale_coords(spacing = 1.0)
# Compute for all node pairs
graph.min_distance()
graph.max_distance()
# Compute only for connected nodes
graph.min_distance(connected = True)
# Compute only for disconnected nodes
graph.min_distance(connected = False)

Two important attributes are node weights and edge weights:

import random
graph.node_weights = {i: random.random() for i in graph.nodes}
graph.edge_weights = {edge: random.random() for edge in graph.sorted_edges}
assert graph.has_coords
assert graph.has_node_weights
assert graph.has_edge_weights

Class constructors can help you create a variety of graphs. A useful constructor is starting from a set of coordinates. By default, it will create an empty set of edges, but we can use the set_ud_edges method to specify the edges as the unit-disk intersections.

from qoolqit import DataGraph
coords = [(-1.0, 0.0), (0.0, 0.0), (1.0, 0.0), (0.0, 1.0), (0.0, -1.0)]
graph = DataGraph.from_coordinates(coords)
assert len(graph.edges) == 0
graph.set_ud_edges(radius = 1.0)
assert len(graph.edges) > 0
graph.draw()

A line graph on n nodes.

graph = DataGraph.line(n = 10, spacing = 1.0)
graph.draw()

A circle graph on n nodes.

graph = DataGraph.circle(n = 10, spacing = 1.0, center = (0.0, 0.0))
graph.draw()

A triangular lattice graph with m rows and n columns of triangles.

graph = DataGraph.triangular(m = 2, n = 2, spacing = 1.0)
graph.draw()

A square lattice graph with m rows and n columns of square.

graph = DataGraph.square(m = 2, n = 2, spacing = 1.0)
graph.draw()

A Hexagonal lattice graph with m rows and n columns of hexagons.

graph = DataGraph.hexagonal(m = 2, n = 2, spacing = 1.0)
graph.draw()

An Heavy-Hexagonal lattice graph with m rows and n columns of hexagons where each edge is decorated with an additional lattice site.

graph = DataGraph.heavy_hexagonal(m = 2, n = 2, spacing = 1.0)
graph.draw()

A random unit-disk graph by uniformly sampling points in area of side L.

graph = DataGraph.random_ud(n = 10, radius = 1.0, L = 2.0)
graph.draw()

Other generic constructors are also available which have no information on node coordinates.

A random Erdős–Rényi graph of n nodes.

graph = DataGraph.random_er(n = 10, p = 0.5, seed = 1)
graph.draw()

Loading an adjacency matrix into a graph is also possible.

  • Given that graphs in QoolQit are undirected, the matrix must be symmetric.
  • As in the standard adjacency matrix interpretation, off-diagonal elements are loaded as edge-weights as long as they are non-zero.
  • Given that QoolQit does not consider graphs with self-loops, diagonal elements are loaded as node-weights.
import numpy as np
n_nodes = 5
data = np.random.rand(n_nodes, n_nodes)
# Matrix must be symmetric
data = data + data.T
graph = DataGraph.from_matrix(data)
assert graph.has_node_weights
assert graph.has_edge_weights
# Setting the diagonal to zero
np.fill_diagonal(data, 0.0)
# Removing the value for the pair (1, 2)
data[1, 2] = 0.0
data[2, 1] = 0.0
graph = DataGraph.from_matrix(data)
# Checking there are no node weights and the edge (1, 2) was not added
assert not graph.has_node_weights
assert (1, 2) not in graph.edges