Skip to content

Results are limited to the current section : Qoolqit

qoolqit

A Python library for algorithm development in the Rydberg Analog Model.

Modules:

Classes:

  • AnalogDevice

    A realistic device for analog sequence execution.

  • Blackman

    A Blackman window of a specified duration and area under the curve.

  • Constant

    A constant waveform over a given duration.

  • DataGraph

    The main graph structure to represent problem data.

  • Delay

    An empty waveform.

  • Device

    QoolQit Device wrapper around a Pulser BaseDevice.

  • DigitalAnalogDevice

    A device with digital and analog capabilities.

  • Drive

    The drive Hamiltonian acting over a duration.

  • Interpolated

    A waveform created from interpolation of a set of data points.

  • MockDevice

    A virtual device for unconstrained prototyping.

  • PiecewiseLinear

    A piecewise linear waveform.

  • QuantumProgram

    A program representing a Sequence acting on a Register of qubits.

  • Ramp

    A ramp that linearly interpolates between an initial and final value.

  • Register

    The Register in QoolQit, representing a set of qubits with coordinates.

  • SequenceCompiler

    Compiles a QoolQit Register and Drive to a Device.

  • Sin

    An arbitrary sine over a given duration.

Functions:

A realistic device for analog sequence execution.

Methods:

  • from_connection

    Return the specified device from the selected device from a connection.

  • info

    Show the device short description and constraints.

  • reset_converter

    Resets the unit converter to the default one.

  • set_distance_unit

    Changes the unit converter according to a reference distance unit.

  • set_energy_unit

    Changes the unit converter according to a reference energy unit.

Attributes:

  • specs (dict[str, float | None]) –

    Return the device specification constraints.

Source code in qoolqit/devices/device.py
def __init__(self) -> None:
super().__init__(pulser_device=pulser.AnalogDevice)

Return the device specification constraints.

from_connection(connection: RemoteConnection, name: str) -> Device classmethod

Section titled “ from_connection(connection: RemoteConnection, name: str) -> Device classmethod ”

Return the specified device from the selected device from a connection.

Available devices through the provided connection are can be seen with the connection.fetch_available_devices() method.

Parameters:

(RemoteConnection) –

connection object to fetch the available devices.

(str) –

The name of the desired device.

Returns:

  • Device ( Device ) –

    The requested device.

Raises:

  • ValueError

    If the requested device is not available through the provided connection.

Example:

from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
Source code in qoolqit/devices/device.py
@classmethod
def from_connection(cls, connection: RemoteConnection, name: str) -> Device:
"""Return the specified device from the selected device from a connection.
Available devices through the provided connection are can be seen with
the `connection.fetch_available_devices()` method.
Args:
connection (RemoteConnection): connection object to fetch the available devices.
name (str): The name of the desired device.
Returns:
Device: The requested device.
Raises:
ValueError: If the requested device is not available through the provided connection.
Example:
```python
from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
```
"""
available_remote_devices = connection.fetch_available_devices()
if name not in available_remote_devices:
raise ValueError(f"Device {name} is not available through the provided connection.")
pulser_device = available_remote_devices[name]
return cls(pulser_device=pulser_device)

Show the device short description and constraints.

Source code in qoolqit/devices/device.py
def info(self) -> None:
"""Show the device short description and constraints."""
print(self)

Resets the unit converter to the default one.

Source code in qoolqit/devices/device.py
def reset_converter(self) -> None:
"""Resets the unit converter to the default one."""
# Create a NEW converter so mutations don't persist.
self._converter = self._default_converter

Changes the unit converter according to a reference distance unit.

Source code in qoolqit/devices/device.py
def set_distance_unit(self, distance: float) -> None:
"""Changes the unit converter according to a reference distance unit."""
self.converter.factors = self.converter.factors_from_distance(distance)

Changes the unit converter according to a reference energy unit.

Source code in qoolqit/devices/device.py
def set_energy_unit(self, energy: float) -> None:
"""Changes the unit converter according to a reference energy unit."""
self.converter.factors = self.converter.factors_from_energy(energy)

A Blackman window of a specified duration and area under the curve.

Implements the Blackman window shaped waveform blackman(t) = A(0.42 - 0.5cos(αt) + 0.08cos(2αt)) A = area/(0.42duration) α = 2π/duration

See: https://en.wikipedia.org/wiki/Window_function#:~:text=Blackman%20window (external)

Parameters:

(float) –

The waveform duration.

(float) –

The integral of the waveform.

Example
blackman_wf = Blackman(100.0, area=3.14)

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/waveforms.py
def __init__(self, duration: float, area: float) -> None:
"""Initializes a new Blackman waveform."""
super().__init__(duration, area=area)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

A constant waveform over a given duration.

Parameters:

(float) –

the total duration.

(float) –

the value to take during the duration.

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
self,
duration: float,
value: float,
) -> None:
super().__init__(duration, value=value)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

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

Delay(duration: float, *args: float, **kwargs: float | np.ndarray)

Section titled “ Delay(duration: float, *args: float, **kwargs: float | np.ndarray) ”

An empty waveform.

Parameters:

(float) –

the total duration of the waveform.

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/base_waveforms.py
def __init__(
self,
duration: float,
*args: float,
**kwargs: float | np.ndarray,
) -> None:
"""Initializes the Waveform.
Arguments:
duration: the total duration of the waveform.
"""
if duration <= 0:
raise ValueError("Duration needs to be a positive non-zero value.")
if len(args) > 0:
raise ValueError(
f"Extra arguments in {type(self).__name__} need to be passed as keyword arguments"
)
self._duration = duration
self._params_dict = kwargs
self._max: float | None = None
self._min: float | None = None
for key, value in kwargs.items():
setattr(self, key, value)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

Device(pulser_device: BaseDevice, default_converter: Optional[UnitConverter] = None)

Section titled “ Device(pulser_device: BaseDevice, default_converter: Optional[UnitConverter] = None) ”

QoolQit Device wrapper around a Pulser BaseDevice.

Parameters:

(BaseDevice) –

a BaseDevice to build the QoolQit device from.

(Optional[UnitConverter], default:None) –

optional unit converter to handle unit conversion.

Examples:

From Pulser device:

qoolqit_device = Device(pulser_device=pulser_device)

From remote Pulser device:

from pulser_pasqal import PasqalCloud
from qoolqit import Device
# Fetch the remote device from the connection
connection = PasqalCloud()
pulser_fresnel_device = connection.fetch_available_devices()["FRESNEL"]
# Wrap a Pulser device object into a QoolQit Device
fresnel_device = Device(pulser_device=PulserFresnelDevice)

From custom Pulser device:

from dataclasses import replace
from pulser import AnalogDevice
from qoolqit import Device
# Converting the pulser Device object in a VirtualDevice object
VirtualAnalog = AnalogDevice.to_virtual()
# Replacing desired values
ModdedAnalogDevice = replace(
VirtualAnalog,
max_radial_distance=100,
max_sequence_duration=7000
)
# Wrap a Pulser device object into a QoolQit Device
mod_analog_device = Device(pulser_device=ModdedAnalogDevice)

Methods:

  • from_connection

    Return the specified device from the selected device from a connection.

  • info

    Show the device short description and constraints.

  • reset_converter

    Resets the unit converter to the default one.

  • set_distance_unit

    Changes the unit converter according to a reference distance unit.

  • set_energy_unit

    Changes the unit converter according to a reference energy unit.

Attributes:

  • specs (dict[str, float | None]) –

    Return the device specification constraints.

Source code in qoolqit/devices/device.py
def __init__(
self,
pulser_device: BaseDevice,
default_converter: Optional[UnitConverter] = None,
) -> None:
if not isinstance(pulser_device, BaseDevice):
raise TypeError("`pulser_device` must be an instance of Pulser BaseDevice class.")
# Store it for all subsequent lookups
self._pulser_device: BaseDevice = pulser_device
self._name: str = self._pulser_device.name
# Physical constants / channel & limit lookups (assumes 'rydberg_global' channel)
self._C6 = self._pulser_device.interaction_coeff
self._clock_period = self._pulser_device.channels["rydberg_global"].clock_period
# Relevant limits from the underlying device (float or None)
self._max_duration = self._pulser_device.max_sequence_duration
self._max_amp = self._pulser_device.channels["rydberg_global"].max_amp
self._upper_amp = self._max_amp or 4 * math.pi
self._max_abs_det = self._pulser_device.channels["rydberg_global"].max_abs_detuning
self._min_distance = self._pulser_device.min_atom_distance
self._lower_distance = self._min_distance or 5.0
self._max_radial_distance = self._pulser_device.max_radial_distance
# ratio between maximum amplitude and maximum interaction energy J_max = C6/r_min^6
self._energy_ratio: float = (self._upper_amp * self._lower_distance**6) / self._C6
# layouts
self._requires_layout = self._pulser_device.requires_layout
if default_converter is not None:
# Snapshot the caller-provided factors so reset() reproduces them exactly.
t0, e0, d0 = default_converter.factors
self._default_factory: Callable[[], UnitConverter] = lambda: UnitConverter(
self._C6, t0, e0, d0
)
else:
self._default_factory = lambda: UnitConverter.from_distance(
self._C6, self._lower_distance
)
self.reset_converter()

Return the device specification constraints.

from_connection(connection: RemoteConnection, name: str) -> Device classmethod

Section titled “ from_connection(connection: RemoteConnection, name: str) -&gt; Device classmethod ”

Return the specified device from the selected device from a connection.

Available devices through the provided connection are can be seen with the connection.fetch_available_devices() method.

Parameters:

(RemoteConnection) –

connection object to fetch the available devices.

(str) –

The name of the desired device.

Returns:

  • Device ( Device ) –

    The requested device.

Raises:

  • ValueError

    If the requested device is not available through the provided connection.

Example:

from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
Source code in qoolqit/devices/device.py
@classmethod
def from_connection(cls, connection: RemoteConnection, name: str) -> Device:
"""Return the specified device from the selected device from a connection.
Available devices through the provided connection are can be seen with
the `connection.fetch_available_devices()` method.
Args:
connection (RemoteConnection): connection object to fetch the available devices.
name (str): The name of the desired device.
Returns:
Device: The requested device.
Raises:
ValueError: If the requested device is not available through the provided connection.
Example:
```python
from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
```
"""
available_remote_devices = connection.fetch_available_devices()
if name not in available_remote_devices:
raise ValueError(f"Device {name} is not available through the provided connection.")
pulser_device = available_remote_devices[name]
return cls(pulser_device=pulser_device)

Show the device short description and constraints.

Source code in qoolqit/devices/device.py
def info(self) -> None:
"""Show the device short description and constraints."""
print(self)

Resets the unit converter to the default one.

Source code in qoolqit/devices/device.py
def reset_converter(self) -> None:
"""Resets the unit converter to the default one."""
# Create a NEW converter so mutations don't persist.
self._converter = self._default_converter

Changes the unit converter according to a reference distance unit.

Source code in qoolqit/devices/device.py
def set_distance_unit(self, distance: float) -> None:
"""Changes the unit converter according to a reference distance unit."""
self.converter.factors = self.converter.factors_from_distance(distance)

Changes the unit converter according to a reference energy unit.

Source code in qoolqit/devices/device.py
def set_energy_unit(self, energy: float) -> None:
"""Changes the unit converter according to a reference energy unit."""
self.converter.factors = self.converter.factors_from_energy(energy)

A device with digital and analog capabilities.

Methods:

  • from_connection

    Return the specified device from the selected device from a connection.

  • info

    Show the device short description and constraints.

  • reset_converter

    Resets the unit converter to the default one.

  • set_distance_unit

    Changes the unit converter according to a reference distance unit.

  • set_energy_unit

    Changes the unit converter according to a reference energy unit.

Attributes:

  • specs (dict[str, float | None]) –

    Return the device specification constraints.

Source code in qoolqit/devices/device.py
def __init__(self) -> None:
super().__init__(pulser_device=pulser.DigitalAnalogDevice)

Return the device specification constraints.

from_connection(connection: RemoteConnection, name: str) -> Device classmethod

Section titled “ from_connection(connection: RemoteConnection, name: str) -&gt; Device classmethod ”

Return the specified device from the selected device from a connection.

Available devices through the provided connection are can be seen with the connection.fetch_available_devices() method.

Parameters:

(RemoteConnection) –

connection object to fetch the available devices.

(str) –

The name of the desired device.

Returns:

  • Device ( Device ) –

    The requested device.

Raises:

  • ValueError

    If the requested device is not available through the provided connection.

Example:

from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
Source code in qoolqit/devices/device.py
@classmethod
def from_connection(cls, connection: RemoteConnection, name: str) -> Device:
"""Return the specified device from the selected device from a connection.
Available devices through the provided connection are can be seen with
the `connection.fetch_available_devices()` method.
Args:
connection (RemoteConnection): connection object to fetch the available devices.
name (str): The name of the desired device.
Returns:
Device: The requested device.
Raises:
ValueError: If the requested device is not available through the provided connection.
Example:
```python
from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
```
"""
available_remote_devices = connection.fetch_available_devices()
if name not in available_remote_devices:
raise ValueError(f"Device {name} is not available through the provided connection.")
pulser_device = available_remote_devices[name]
return cls(pulser_device=pulser_device)

Show the device short description and constraints.

Source code in qoolqit/devices/device.py
def info(self) -> None:
"""Show the device short description and constraints."""
print(self)

Resets the unit converter to the default one.

Source code in qoolqit/devices/device.py
def reset_converter(self) -> None:
"""Resets the unit converter to the default one."""
# Create a NEW converter so mutations don't persist.
self._converter = self._default_converter

Changes the unit converter according to a reference distance unit.

Source code in qoolqit/devices/device.py
def set_distance_unit(self, distance: float) -> None:
"""Changes the unit converter according to a reference distance unit."""
self.converter.factors = self.converter.factors_from_distance(distance)

Changes the unit converter according to a reference energy unit.

Source code in qoolqit/devices/device.py
def set_energy_unit(self, energy: float) -> None:
"""Changes the unit converter according to a reference energy unit."""
self.converter.factors = self.converter.factors_from_energy(energy)

Drive(*, amplitude: Waveform, detuning: Waveform | None = None, dmm: DetuningMapModulator | None = None, phase: float = 0.0)

Section titled “ Drive(*, amplitude: Waveform, detuning: Waveform | None = None, dmm: DetuningMapModulator | None = None, phase: float = 0.0) ”

The drive Hamiltonian acting over a duration.

The Drive specifies the control parameters for the time-dependent drive Hamiltonian in the Rydberg model (see https://docs.pasqal.com/qoolqit/get_started/qoolqit_model/ (external) for details),

H_drive(t) = Σᵢ [Ω(t)/2 (cos φ(t) σˣᵢ - sin φ(t) σʸᵢ)] - Σᵢ [δ(t) + εᵢ Δ(t)] nᵢ

representing: - Amplitude Ω(t): Controls the Rabi frequency that drives qubits. - Detuning δ(t): Controls the energy offset of the Rydberg state. - dmm εᵢ, Δ(t): Detuning Map Modulator (DMM) for additional qubit-specific detunings. - Phase φ: Global phase applied to the amplitude term.

Parameters:

(Waveform) –

Time-dependent amplitude waveform Ω(t) representing the Rabi frequency. Controls the strength of the coupling between ground and Rydberg states. Must be positive for all times.

(Waveform | None, default:None) –

Time-dependent detuning waveform δ(t) representing the energy offset of the Rydberg state relative to resonance. If None, defaults to zero detuning (Delay waveform) for the duration of the amplitude.

(DetuningMapModulator | None, default:None) –

DetuningMapModulator instance for additional negative detuning waveform Δ(t) ≤ 0 applied to individual qubits as specified by its weights attribute εᵢ.

(float, default:0.0) –

Global phase φ applied to the amplitude term in the Hamiltonian. Defaults to 0.0 (no phase).

Raises:

  • TypeError

    If amplitude or detuning are not Waveform instances.

  • ValueError

    If the amplitude waveform has negative values.

Note
Example

Attributes:

  • amplitude (Waveform) –

    The amplitude waveform in the drive.

  • detuning (Waveform) –

    The detuning waveform in the drive.

  • dmm (DetuningMapModulator | None) –

    Detuning Map Modulator (DMM) applied to individual qubits.

  • phase (float) –

    The phase value in the drive.

Source code in qoolqit/drive.py
def __init__(
self,
*,
amplitude: Waveform,
detuning: Waveform | None = None,
dmm: DetuningMapModulator | None = None,
phase: float = 0.0,
) -> None:
"""Initialize a Drive.
The Drive specifies the control parameters for the time-dependent drive Hamiltonian
in the Rydberg model
(see https://docs.pasqal.com/qoolqit/get_started/qoolqit_model/ for details),
H_drive(t) = Σᵢ [Ω(t)/2 (cos φ(t) σˣᵢ - sin φ(t) σʸᵢ)] - Σᵢ [δ(t) + εᵢ Δ(t)] nᵢ
representing:
- Amplitude Ω(t): Controls the Rabi frequency that drives qubits.
- Detuning δ(t): Controls the energy offset of the Rydberg state.
- dmm εᵢ, Δ(t): Detuning Map Modulator (DMM) for additional qubit-specific detunings.
- Phase φ: Global phase applied to the amplitude term.
Args:
amplitude: Time-dependent amplitude waveform Ω(t) representing the Rabi frequency.
Controls the strength of the coupling between ground and Rydberg states.
Must be positive for all times.
detuning: Time-dependent detuning waveform δ(t) representing the energy offset
of the Rydberg state relative to resonance. If None, defaults to zero
detuning (Delay waveform) for the duration of the amplitude.
dmm: DetuningMapModulator instance for additional negative detuning waveform Δ(t) ≤ 0
applied to individual qubits as specified by its `weights` attribute εᵢ.
phase: Global phase φ applied to the amplitude term in the Hamiltonian.
Defaults to 0.0 (no phase).
Raises:
TypeError: If amplitude or detuning are not Waveform instances.
ValueError: If the amplitude waveform has negative values.
Note:
- All arguments must be passed as keyword arguments.
- If amplitude and detuning have different durations, the shorter one is
automatically extended with a Delay to match the longer duration.
- DetuningMapModulator waveform must be negative for all times
(≤ 0) as it represents energy shifts below the resonance.
Example:
>>> from qoolqit import Drive
>>> from qoolqit.waveforms import Constant, Ramp
>>>
>>> # Simple constant drive
>>> drive = Drive(amplitude=Constant(10.0, 1.5))
>>>
>>> # Drive with time-varying amplitude and detuning
>>> amp = Ramp(5.0, 0.0, 2.0)
>>> det = Constant(5.0, -1.0)
>>> drive = Drive(amplitude=amp, detuning=det, phase=0.5)
"""
for arg in [amplitude, detuning]:
if arg is not None and not isinstance(arg, Waveform):
raise TypeError("'amplitude' and 'detuning' must be of type Waveform.")
if amplitude.min() < 0.0:
raise ValueError("'amplitude' must be positive.")
self._amplitude = amplitude
self._detuning = detuning if detuning is not None else Delay(amplitude.duration)
self._amplitude_orig = self._amplitude
self._detuning_orig = self._detuning
# adjust amplitude and detuning waveforms to match the duration
if self._amplitude.duration > self._detuning.duration:
extra_duration = self._amplitude.duration - self._detuning.duration
self._detuning = CompositeWaveform(self._detuning, Delay(extra_duration))
elif self._detuning.duration > self._amplitude.duration:
extra_duration = self._detuning.duration - self._amplitude.duration
self._amplitude = CompositeWaveform(self._amplitude, Delay(extra_duration))
self._duration = self._amplitude.duration
if dmm is not None and not isinstance(dmm, DetuningMapModulator):
raise TypeError("'dmm' must be of type DetuningMapModulator.")
self._dmm = dmm
self._phase = phase

The amplitude waveform in the drive.

The detuning waveform in the drive.

dmm: DetuningMapModulator | None property

Section titled “ dmm: DetuningMapModulator | None property ”

Detuning Map Modulator (DMM) applied to individual qubits.

The phase value in the drive.

Interpolated(duration: float, values: ArrayLike, times: Optional[ArrayLike] = None, interpolator: str = 'PchipInterpolator', **interpolator_kwargs: Any)

Section titled “ Interpolated(duration: float, values: ArrayLike, times: Optional[ArrayLike] = None, interpolator: str = &#39;PchipInterpolator&#39;, **interpolator_kwargs: Any) ”

A waveform created from interpolation of a set of data points.

Parameters:

(int) –

The waveform duration (in ns).

(ArrayLike) –

Values of the interpolation points. Must be a list of castable to float or a parametrized object.

(ArrayLike, default:None) –

Fractions of the total duration (between 0 and 1), indicating where to place each value on the time axis. Must be a list of castable to float or a parametrized object. If not given, the values are spread evenly throughout the full duration of the waveform.

(str, default:'PchipInterpolator') –

The SciPy interpolation class to use. Supports "PchipInterpolator" and "interp1d".

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
self,
duration: float,
values: ArrayLike,
times: Optional[ArrayLike] = None,
interpolator: str = "PchipInterpolator",
**interpolator_kwargs: Any,
):
"""Initializes a new Interpolated waveform."""
super().__init__(duration)
self._values = np.array(values, dtype=float)
if times: # fractional times in [0,1]
if any([(ft < 0) or (ft > 1) for ft in times]):
raise ValueError("All values in `times` must be in [0,1].")
self._times = np.array(times, dtype=float)
if len(times) != len(self._values):
raise ValueError(
"Arguments `values` and `times` must be arrays of the same length."
)
else:
self._times = np.linspace(0, 1, num=len(self._values))
if interpolator not in self._valid_interpolators:
raise ValueError(
f"Invalid interpolator '{interpolator}', only "
"accepts: " + ", ".join(self._valid_interpolators)
)
self._interpolator = interpolator
self._interpolator_kwargs = interpolator_kwargs
interp_cls = getattr(interpolate, interpolator)
self._interp_func = interp_cls(duration * self._times, self._values, **interpolator_kwargs)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

A virtual device for unconstrained prototyping.

Methods:

  • from_connection

    Return the specified device from the selected device from a connection.

  • info

    Show the device short description and constraints.

  • reset_converter

    Resets the unit converter to the default one.

  • set_distance_unit

    Changes the unit converter according to a reference distance unit.

  • set_energy_unit

    Changes the unit converter according to a reference energy unit.

Attributes:

  • specs (dict[str, float | None]) –

    Return the device specification constraints.

Source code in qoolqit/devices/device.py
def __init__(self) -> None:
super().__init__(pulser_device=pulser.MockDevice)

Return the device specification constraints.

from_connection(connection: RemoteConnection, name: str) -> Device classmethod

Section titled “ from_connection(connection: RemoteConnection, name: str) -&gt; Device classmethod ”

Return the specified device from the selected device from a connection.

Available devices through the provided connection are can be seen with the connection.fetch_available_devices() method.

Parameters:

(RemoteConnection) –

connection object to fetch the available devices.

(str) –

The name of the desired device.

Returns:

  • Device ( Device ) –

    The requested device.

Raises:

  • ValueError

    If the requested device is not available through the provided connection.

Example:

from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
Source code in qoolqit/devices/device.py
@classmethod
def from_connection(cls, connection: RemoteConnection, name: str) -> Device:
"""Return the specified device from the selected device from a connection.
Available devices through the provided connection are can be seen with
the `connection.fetch_available_devices()` method.
Args:
connection (RemoteConnection): connection object to fetch the available devices.
name (str): The name of the desired device.
Returns:
Device: The requested device.
Raises:
ValueError: If the requested device is not available through the provided connection.
Example:
```python
from pulser_pasqal import PasqalCloud
fresnel_device = Device.from_connection(connection=PasqalCloud(), name="FRESNEL")
```
"""
available_remote_devices = connection.fetch_available_devices()
if name not in available_remote_devices:
raise ValueError(f"Device {name} is not available through the provided connection.")
pulser_device = available_remote_devices[name]
return cls(pulser_device=pulser_device)

Show the device short description and constraints.

Source code in qoolqit/devices/device.py
def info(self) -> None:
"""Show the device short description and constraints."""
print(self)

Resets the unit converter to the default one.

Source code in qoolqit/devices/device.py
def reset_converter(self) -> None:
"""Resets the unit converter to the default one."""
# Create a NEW converter so mutations don't persist.
self._converter = self._default_converter

Changes the unit converter according to a reference distance unit.

Source code in qoolqit/devices/device.py
def set_distance_unit(self, distance: float) -> None:
"""Changes the unit converter according to a reference distance unit."""
self.converter.factors = self.converter.factors_from_distance(distance)

Changes the unit converter according to a reference energy unit.

Source code in qoolqit/devices/device.py
def set_energy_unit(self, energy: float) -> None:
"""Changes the unit converter according to a reference energy unit."""
self.converter.factors = self.converter.factors_from_energy(energy)

PiecewiseLinear(durations: list | tuple, values: list | tuple)

Section titled “ PiecewiseLinear(durations: list | tuple, values: list | tuple) ”

A piecewise linear waveform.

Creates a composite waveform of N ramps that linearly interpolate through the given N+1 values.

Parameters:

(list | tuple) –

list or tuple of N duration values.

(list | tuple) –

list or tuple of N+1 waveform values.

Methods:

  • function

    Identifies the right waveform in the composition and evaluates it at time t.

  • max

    Get the maximum value of the waveform.

  • min

    Get the approximate minimum value of the waveform.

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • durations (list[float]) –

    Returns the list of durations of each individual waveform.

  • n_waveforms (int) –

    Returns the number of waveforms.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

  • times (list[float]) –

    Returns the list of times when each individual waveform starts.

  • waveforms (list[Waveform]) –

    Returns a list of the individual waveforms.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
self,
durations: list | tuple,
values: list | tuple,
) -> None:
if not (isinstance(durations, (list, tuple)) or isinstance(values, (list, tuple))):
raise TypeError(
"A PiecewiseLinear waveform requires a list or tuple of durations and values."
)
if len(durations) + 1 != len(values) or len(durations) == 1:
raise ValueError(
"A PiecewiseLinear waveform requires N durations and N + 1 values, for N >= 2."
)
for duration in durations:
if duration == 0.0:
raise ValueError("A PiecewiseLinear interval cannot have zero duration.")
self.values = values
wfs = [Ramp(dur, values[i], values[i + 1]) for i, dur in enumerate(durations)]
super().__init__(*wfs)

Returns the duration of the waveform.

Returns the list of durations of each individual waveform.

Returns the number of waveforms.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

Returns the list of times when each individual waveform starts.

Returns a list of the individual waveforms.

Identifies the right waveform in the composition and evaluates it at time t.

Source code in qoolqit/waveforms/base_waveforms.py
def function(self, t: float) -> float:
"""Identifies the right waveform in the composition and evaluates it at time t."""
idx = np.searchsorted(self.times, t, side="right") - 1
if idx == -1:
return 0.0
if idx == self.n_waveforms:
if t == self.times[-1]:
idx = idx - 1
else:
return 0.0
local_t = t - self.times[idx]
value: float = self.waveforms[idx](local_t)
return value

Get the maximum value of the waveform.

Source code in qoolqit/waveforms/base_waveforms.py
def max(self) -> float:
"""Get the maximum value of the waveform."""
return max([wf.max() for wf in self.waveforms])

Get the approximate minimum value of the waveform.

This is a brute-force method that samples the waveform over a pre-defined number of points to find the minimum value in the duration. Custom waveforms that have an easy to compute maximum value should override this method.

Source code in qoolqit/waveforms/base_waveforms.py
def min(self) -> float:
"""Get the approximate minimum value of the waveform.
This is a brute-force method that samples the waveform over a
pre-defined number of points to find the minimum value in the
duration. Custom waveforms that have an easy to compute
maximum value should override this method.
"""
if self._min is None:
self._approximate_min_max()
return cast(float, self._min)

QuantumProgram(register: Register, drive: Drive)

Section titled “ QuantumProgram(register: Register, drive: Drive) ”

A program representing a Sequence acting on a Register of qubits.

Parameters:

(Register) –

the register of qubits, defining their positions.

(Drive) –

the drive acting on qubits, defining amplitude, detuning and phase.

Methods:

  • compile_to

    Compiles the quantum program for execution on a specific device.

Attributes:

Source code in qoolqit/program.py
def __init__(
self,
register: Register,
drive: Drive,
) -> None:
if not isinstance(register, Register):
raise TypeError("`register` must be of type Register.")
self._register = register
if not isinstance(drive, Drive):
raise TypeError("`drive` must be of type Drive.")
if drive.dmm is not None:
dmm_weights = drive.dmm.weights
for qid in dmm_weights.keys():
if qid not in register.qubits:
raise ValueError(
"In this QuantumProgram, the drive's detuning modulator map (DMM) "
f"and the register do not match: qubit {qid} appears in the DMM "
"but is not defined in the register."
)
self._drive = drive
self._compiled_sequence: PulserSequence | None = None

compiled_sequence: PulserSequence property

Section titled “ compiled_sequence: PulserSequence property ”

The Pulser sequence compiled to a specific device.

The driving waveforms.

Check if the program has been compiled.

The register of qubits.

compile_to(device: Device, profile: CompilerProfile = CompilerProfile.MAX_ENERGY, device_max_duration_ratio: float | None = None) -> None

Section titled “ compile_to(device: Device, profile: CompilerProfile = CompilerProfile.MAX_ENERGY, device_max_duration_ratio: float | None = None) -&gt; None ”

Compiles the quantum program for execution on a specific device.

The compilation process adapts the program to the device's constraints while preserving the relative ratios of the original program parameters. Different compilation profiles optimize for specific objectives:

  • CompilerProfile.MAX_ENERGY (default): Scales the program to utilize the device's maximum capabilities. The drive amplitude and the register positions are rescaled to achieve respectively the maximum amplitude and the minimum pairwise distance compatible with the input program and the device's constraints.
  • CompilerProfile.WORKING_POINT: .

Further options DO NOT preserve the input program, but rather adapts the program to the device's constraint. Programs compiled this way are not guaranteed to be portable across devices.

  • device_max_duration_ratio: Rescale the drive duration to a fraction of the device's maximum allowed duration. This option is useful in adiabatic protocols where one simply seek to minimize the time derivative of the drive's amplitude.

Parameters:

(Device) –

The target device for compilation.

(CompilerProfile, default:MAX_ENERGY) –

The compilation strategy to optimize the program. Defaults to CompilerProfile.MAX_ENERGY.

(float | None, default:None) –

Whether to set the program duration to a fraction of the device's maximum allowed duration. Must be a number in the range (0, 1]. Can only be set if the device has a maximum allowed duration.

Raises:

Source code in qoolqit/program.py
def compile_to(
self,
device: Device,
profile: CompilerProfile = CompilerProfile.MAX_ENERGY,
device_max_duration_ratio: float | None = None,
) -> None:
"""Compiles the quantum program for execution on a specific device.
The compilation process adapts the program to the device's constraints while
preserving the relative ratios of the original program parameters. Different
compilation profiles optimize for specific objectives:
- CompilerProfile.MAX_ENERGY (default): Scales the program to utilize the device's
maximum capabilities. The drive amplitude and the register positions are rescaled
to achieve respectively the maximum amplitude and the minimum pairwise distance
compatible with the input program and the device's constraints.
- CompilerProfile.WORKING_POINT: .
Further options DO NOT preserve the input program, but rather adapts the program to
the device's constraint. Programs compiled this way are not guaranteed to be portable
across devices.
- device_max_duration_ratio: Rescale the drive duration to a fraction of the
device's maximum allowed duration.
This option is useful in adiabatic protocols where one simply seek to
minimize the time derivative of the drive's amplitude.
Args:
device: The target device for compilation.
profile: The compilation strategy to optimize the program.
Defaults to CompilerProfile.MAX_ENERGY.
device_max_duration_ratio: Whether to set the program duration to a fraction of
the device's maximum allowed duration. Must be a number in the range (0, 1].
Can only be set if the device has a maximum allowed duration.
Raises:
CompilationError: If the compilation fails due to device constraints.
"""
if device_max_duration_ratio is not None:
if device._max_duration is None:
raise ValueError(
"Cannot set `device_max_duration_ratio` because the target device "
"does not have a maximum allowed duration."
)
if not (0 < device_max_duration_ratio <= 1):
raise ValueError(
"`device_max_duration_ratio` must be between 0 and 1, "
f"got {device_max_duration_ratio} instead."
)
# Check if device supports DMM and has a DMM channel
if self.drive.dmm is not None:
if not device._device.dmm_channels:
raise CompilationError(
"The device does not support DMM. Please use a device that supports DMM."
)
compiler = SequenceCompiler(
self.register, self.drive, device, profile, device_max_duration_ratio
)
self._device = device
self._compiled_sequence = compiler.compile_sequence()

Ramp(duration: float, initial_value: float, final_value: float)

Section titled “ Ramp(duration: float, initial_value: float, final_value: float) ”

A ramp that linearly interpolates between an initial and final value.

Parameters:

(float) –

the total duration.

(float) –

the initial value at t = 0.

(float) –

the final value at t = duration.

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
self,
duration: float,
initial_value: float,
final_value: float,
) -> None:
super().__init__(duration, initial_value=initial_value, final_value=final_value)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

The Register in QoolQit, representing a set of qubits with coordinates.

Parameters:

(dict) –

a dictionary of qubits and respective coordinates {q: (x, y), ...}.

Methods:

Attributes:

  • n_qubits (int) –

    Number of qubits in the Register.

  • qubits (dict) –

    Returns the dictionary of qubits and respective coordinates.

  • qubits_ids (list) –

    Returns the qubit keys.

Source code in qoolqit/register.py
def __init__(self, qubits: dict) -> None:
"""Default constructor for the Register.
Arguments:
qubits: a dictionary of qubits and respective coordinates {q: (x, y), ...}.
"""
if not isinstance(qubits, dict):
raise TypeError(
"Register must be initialized with a dictionary of "
"qubits and respective coordinates {q: (x, y), ...}."
)
self._qubits: dict = qubits

Number of qubits in the Register.

Returns the dictionary of qubits and respective coordinates.

Returns the qubit keys.

Distance between each qubit pair.

Source code in qoolqit/register.py
def distances(self) -> dict:
"""Distance between each qubit pair."""
pairs = all_node_pairs(list(self.qubits.keys()))
return distances(self.qubits, pairs)

draw(return_fig: bool = False) -> Figure | None

Section titled “ draw(return_fig: bool = False) -&gt; Figure | None ”

Draw the register.

Parameters:

(bool, default:False) –

boolean argument to return the matplotlib figure.

Source code in qoolqit/register.py
def draw(self, return_fig: bool = False) -> Figure | None:
"""Draw the register.
Arguments:
return_fig: boolean argument to return the matplotlib figure.
"""
fig, ax = plt.subplots(1, 1, figsize=(4, 4), dpi=150)
ax.set_aspect("equal")
x_coords, y_coords = zip(*self.qubits.values())
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
grid_x_min, grid_x_max = min(-1, x_min), max(1, x_max)
grid_y_min, grid_y_max = min(-1, y_min), max(1, y_max)
grid_scale = ceil(max(grid_x_max - grid_x_min, grid_y_max - grid_y_min))
ax.grid(True, color="lightgray", linestyle="--", linewidth=0.7)
ax.set_axisbelow(True)
ax.set_xlabel("x")
ax.set_ylabel("y")
eps = 0.05 * grid_scale
ax.set_xlim(grid_x_min - eps, grid_x_max + eps)
ax.set_ylim(grid_y_min - eps, grid_y_max + eps)
possible_multiples = [0.2, 0.25, 0.5, 1.0, 2.0, 2.5, 5.0, 10.0]
grid_multiple = min(possible_multiples, key=lambda x: abs(x - grid_scale / 8))
majorLocatorX = MultipleLocator(grid_multiple)
majorLocatorY = MultipleLocator(grid_multiple)
ax.xaxis.set_major_locator(majorLocatorX)
ax.yaxis.set_major_locator(majorLocatorY)
ax.scatter(x_coords, y_coords, s=50, color="darkgreen")
ax.tick_params(axis="both", which="both", labelbottom=True, labelleft=True, labelsize=8)
if return_fig:
plt.close()
return fig
else:
return None

from_coordinates(coords: list) -> Register classmethod

Section titled “ from_coordinates(coords: list) -&gt; Register classmethod ”

Initializes a Register from a list of coordinates.

Parameters:

(list) –

a list of coordinates [(x, y), ...]

Source code in qoolqit/register.py
@classmethod
def from_coordinates(cls, coords: list) -> Register:
"""Initializes a Register from a list of coordinates.
Arguments:
coords: a list of coordinates [(x, y), ...]
"""
if not isinstance(coords, list):
raise TypeError(
"Register must be initialized with a dictionary of qubit and coordinates."
)
coords_dict = {i: pos for i, pos in enumerate(coords)}
return cls(coords_dict)

from_graph(graph: DataGraph) -> Register classmethod

Section titled “ from_graph(graph: DataGraph) -&gt; Register classmethod ”

Initializes a Register from a graph that has coordinates.

Parameters:

(DataGraph) –

a DataGraph instance.

Source code in qoolqit/register.py
@classmethod
def from_graph(cls, graph: DataGraph) -> Register:
"""Initializes a Register from a graph that has coordinates.
Arguments:
graph: a DataGraph instance.
"""
if not graph.has_coords:
raise ValueError("Initializing a register from a graph requires node coordinates.")
if len(graph.nodes) == 0:
raise ValueError("Trying to initialize a register from an empty graph.")
return cls(graph.coords)

Interaction 1/r^6 between each qubit pair.

Source code in qoolqit/register.py
def interactions(self) -> dict:
"""Interaction 1/r^6 between each qubit pair."""
return {p: 1.0 / (r**6) for p, r in self.distances().items()}

Maximum radial distance between all qubits.

Source code in qoolqit/register.py
def max_radial_distance(self) -> float:
"""Maximum radial distance between all qubits."""
max_radial_distance: float = max(self.radial_distances().values())
return max_radial_distance

Minimum distance between all qubit pairs.

Source code in qoolqit/register.py
def min_distance(self) -> float:
"""Minimum distance between all qubit pairs."""
distance: float = min(self.distances().values())
return distance

Radial distance of each qubit from the origin.

Source code in qoolqit/register.py
def radial_distances(self) -> dict:
"""Radial distance of each qubit from the origin."""
return radial_distances(self.qubits)

SequenceCompiler(register: Register, drive: Drive, device: Device, profile: CompilerProfile, device_max_duration_ratio: float | None = None)

Section titled “ SequenceCompiler(register: Register, drive: Drive, device: Device, profile: CompilerProfile, device_max_duration_ratio: float | None = None) ”

Compiles a QoolQit Register and Drive to a Device.

Parameters:

(Register) –

the QoolQit Register.

(Drive) –

the QoolQit Drive.

(Device) –

the QoolQit Device.

(CompilerProfile) –

the CompilerProfile to use.

(float | None, default:None) –

optionally set the program duration to a fraction of the device's maximum allowed duration.

Source code in qoolqit/execution/sequence_compiler.py
def __init__(
self,
register: Register,
drive: Drive,
device: Device,
profile: CompilerProfile,
device_max_duration_ratio: float | None = None,
) -> None:
"""Initializes the compiler.
Args:
register: the QoolQit Register.
drive: the QoolQit Drive.
device: the QoolQit Device.
profile: the CompilerProfile to use.
device_max_duration_ratio: optionally set the program duration to a fraction
of the device's maximum allowed duration.
"""
self._register = register
self._drive = drive
self._device = device
self._target_device = device._device
self._profile = profile
self._device_max_duration_ratio = device_max_duration_ratio
self._compilation_function: Callable = basic_compilation

Sin(duration: float, amplitude: float = 1.0, omega: float = 1.0, phi: float = 0.0, shift: float = 0.0)

Section titled “ Sin(duration: float, amplitude: float = 1.0, omega: float = 1.0, phi: float = 0.0, shift: float = 0.0) ”

An arbitrary sine over a given duration.

Parameters:

(float) –

the total duration.

(float, default:1.0) –

the amplitude of the sine wave.

(float, default:1.0) –

the frequency of the sine wave.

(float, default:0.0) –

the phase of the sine wave.

(float, default:0.0) –

the vertical shift of the sine wave.

Methods:

  • max

    Get the approximate maximum value of the waveform.

  • min

    Get the approximate minimum value of the waveform.

Attributes:

  • duration (float) –

    Returns the duration of the waveform.

  • params (dict[str, float | ndarray]) –

    Dictionary of parameters used by the waveform.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
self,
duration: float,
amplitude: float = 1.0,
omega: float = 1.0,
phi: float = 0.0,
shift: float = 0.0,
) -> None:
super().__init__(duration, amplitude=amplitude, omega=omega, phi=phi, shift=shift)

Returns the duration of the waveform.

params: dict[str, float | np.ndarray] property

Section titled “ params: dict[str, float | np.ndarray] property ”

Dictionary of parameters used by the waveform.

Get the approximate maximum value of the waveform.

This is a brute-force method that samples the waveform over a pre-defined number of points to find the maximum value in the duration. Custom waveforms that have an easy to compute maximum value should override this method.

Source code in qoolqit/waveforms/base_waveforms.py
def max(self) -> float:
"""Get the approximate maximum value of the waveform.
This is a brute-force method that samples the waveform over a
pre-defined number of points to find the maximum value in the
duration. Custom waveforms that have an easy to compute
maximum value should override this method.
"""
if self._max is None:
self._approximate_min_max()
return cast(float, self._max)

Get the approximate minimum value of the waveform.

This is a brute-force method that samples the waveform over a pre-defined number of points to find the minimum value in the duration. Custom waveforms that have an easy to compute maximum value should override this method.

Source code in qoolqit/waveforms/base_waveforms.py
def min(self) -> float:
"""Get the approximate minimum value of the waveform.
This is a brute-force method that samples the waveform over a
pre-defined number of points to find the minimum value in the
duration. Custom waveforms that have an easy to compute
maximum value should override this method.
"""
if self._min is None:
self._approximate_min_max()
return cast(float, self._min)

Show the default available devices in QooQit.

Source code in qoolqit/devices/device.py
def available_default_devices() -> None:
"""Show the default available devices in QooQit."""
for dev in (AnalogDevice(), DigitalAnalogDevice(), MockDevice()):
dev.info()