Skip to content

qoolqit

qoolqit

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

Modules:

  • devices
  • drive
  • embedding

    Collection of graph and matrix embedding algorithms.

  • exceptions
  • execution

    Execute quantum programs on QPUs or local/remote emulators.

  • graphs

    Graph creation and manipulation in QoolQit.

  • program
  • register
  • visualization

    Visualization helpers for QoolQit results.

  • waveforms

    Composable, time-bounded scalar waveforms for pulse-level quantum control.

Classes:

  • AnalogDevice

    A realistic device for analog sequence execution.

  • AnalogDeviceWithDMM

    A realistic device with DMM for analog sequence execution.

  • BlackmanWaveform

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

  • ConstantWaveform

    A constant waveform over a given duration.

  • DelayWaveform

    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.

  • InterpolatedWaveform

    A waveform created from shape-preserving interpolation of data points.

  • MockDevice

    A virtual device for unconstrained prototyping.

  • PiecewiseLinearWaveform

    A piecewise linear waveform.

  • QuantumProgram

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

  • RampWaveform

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

  • Register

    A QoolQit register mapping qubit IDs to 2D coordinates.

  • SequenceCompiler

    Compiles a QoolQit Register and Drive to a Device.

Functions:

AnalogDevice

AnalogDevice()

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)

specs property

specs: dict[str, float | None]

Return the device specification constraints.

from_connection classmethod

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

Parameters:

  • connection (RemoteConnection) –

    connection object to fetch the available devices.

  • name (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 pasqal_cloud import PasqalCloudConnection
fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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 pasqal_cloud import PasqalCloudConnection
    fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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)

info

info() -> None

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)

reset_converter

reset_converter() -> None

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

set_distance_unit

set_distance_unit(distance: float) -> None

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)

set_energy_unit

set_energy_unit(energy: float) -> None

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)

AnalogDeviceWithDMM

AnalogDeviceWithDMM()

A realistic device with DMM 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:
    dmm_channel = pulser.channels.dmm.DMM(
        clock_period=4,
        min_duration=16,
        max_duration=6000,
        mod_bandwidth=8,
        bottom_detuning=-2 * math.pi * 20,
        total_bottom_detuning=-2 * math.pi * 20,
    )
    # Create a virtual device that can be modified to add a DMM channel.
    pulser_virtual_device = pulser.AnalogDevice.to_virtual()
    pulser_device = replace(
        pulser_virtual_device, dmm_objects=(dmm_channel,), name="AnalogDeviceWithDMM"
    )
    super().__init__(pulser_device=pulser_device)

specs property

specs: dict[str, float | None]

Return the device specification constraints.

from_connection classmethod

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

Parameters:

  • connection (RemoteConnection) –

    connection object to fetch the available devices.

  • name (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 pasqal_cloud import PasqalCloudConnection
fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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 pasqal_cloud import PasqalCloudConnection
    fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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)

info

info() -> None

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)

reset_converter

reset_converter() -> None

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

set_distance_unit

set_distance_unit(distance: float) -> None

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)

set_energy_unit

set_energy_unit(energy: float) -> None

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)

BlackmanWaveform

BlackmanWaveform(duration: float, area: float)

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

Implements the positive 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

Parameters:

  • duration (float) –

    The waveform duration.

  • area (float) –

    The integral of the waveform.

Example
blackman_wf = BlackmanWaveform(100.0, area=3.14)

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • __rshift__

    Returns a new CompositeWaveform composed of this waveform and another.

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 BlackmanWaveform."""
    super().__init__(duration, area=float(area))

duration property

duration: float

Returns the duration of the waveform.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

__rshift__

__rshift__(other: Waveform) -> CompositeWaveform

Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/base_waveforms.py
def __rshift__(self, other: Waveform) -> CompositeWaveform:
    """Returns a new CompositeWaveform composed of this waveform and another."""
    if isinstance(other, Waveform):
        if isinstance(other, CompositeWaveform):
            return CompositeWaveform(self, *other._waveforms)
        return CompositeWaveform(self, other)
    else:
        raise NotImplementedError(f"Composing with object of type {type(other)} not supported.")

ConstantWaveform

ConstantWaveform(duration: float, value: float)

A constant waveform over a given duration.

Parameters:

  • duration (float) –

    the total duration.

  • value (float) –

    the value to take during the duration.

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • __rshift__

    Returns a new CompositeWaveform composed of this waveform and another.

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=float(value))

duration property

duration: float

Returns the duration of the waveform.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

__rshift__

__rshift__(other: Waveform) -> CompositeWaveform

Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/base_waveforms.py
def __rshift__(self, other: Waveform) -> CompositeWaveform:
    """Returns a new CompositeWaveform composed of this waveform and another."""
    if isinstance(other, Waveform):
        if isinstance(other, CompositeWaveform):
            return CompositeWaveform(self, *other._waveforms)
        return CompositeWaveform(self, other)
    else:
        raise NotImplementedError(f"Composing with object of type {type(other)} not supported.")

DelayWaveform

DelayWaveform(
    duration: float, *args: float, **kwargs: float | ndarray
)

An empty waveform.

Parameters:

  • duration (float) –

    the total duration of the waveform.

  • **kwargs (float | ndarray, default: {} ) –

    optional keyword arguments for the waveform function.

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • __rshift__

    Returns a new CompositeWaveform composed of this waveform and another.

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.

    Args:
        duration: the total duration of the waveform.
        **kwargs: optional keyword arguments for the waveform function.
    """

    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 = float(duration)
    self._params_dict = kwargs

    for key, value in kwargs.items():
        setattr(self, key, value)

duration property

duration: float

Returns the duration of the waveform.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

__rshift__

__rshift__(other: Waveform) -> CompositeWaveform

Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/base_waveforms.py
def __rshift__(self, other: Waveform) -> CompositeWaveform:
    """Returns a new CompositeWaveform composed of this waveform and another."""
    if isinstance(other, Waveform):
        if isinstance(other, CompositeWaveform):
            return CompositeWaveform(self, *other._waveforms)
        return CompositeWaveform(self, other)
    else:
        raise NotImplementedError(f"Composing with object of type {type(other)} not supported.")

Device

Device(
    pulser_device: BaseDevice,
    default_converter: UnitConverter | None = None,
)

QoolQit Device wrapper around a Pulser BaseDevice.

Parameters:

  • pulser_device (BaseDevice) –

    a BaseDevice to build the QoolQit device from.

  • default_converter (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 pasqal_cloud import PasqalCloudConnection
from qoolqit import Device

# Fetch the remote device from the connection
connection = PasqalCloudConnection()
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: UnitConverter | None = 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()

specs property

specs: dict[str, float | None]

Return the device specification constraints.

from_connection classmethod

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

Parameters:

  • connection (RemoteConnection) –

    connection object to fetch the available devices.

  • name (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 pasqal_cloud import PasqalCloudConnection
fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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 pasqal_cloud import PasqalCloudConnection
    fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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)

info

info() -> None

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)

reset_converter

reset_converter() -> None

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

set_distance_unit

set_distance_unit(distance: float) -> None

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)

set_energy_unit

set_energy_unit(energy: float) -> None

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)

DigitalAnalogDevice

DigitalAnalogDevice()

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)

specs property

specs: dict[str, float | None]

Return the device specification constraints.

from_connection classmethod

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

Parameters:

  • connection (RemoteConnection) –

    connection object to fetch the available devices.

  • name (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 pasqal_cloud import PasqalCloudConnection
fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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 pasqal_cloud import PasqalCloudConnection
    fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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)

info

info() -> None

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)

reset_converter

reset_converter() -> None

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

set_distance_unit

set_distance_unit(distance: float) -> None

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)

set_energy_unit

set_energy_unit(energy: float) -> None

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

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/ 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:

  • amplitude (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.

  • detuning (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.

  • dmm (DetuningMapModulator | None, default: None ) –

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

  • phase (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
  • 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 ConstantWaveform, RampWaveform

Simple constant drive

drive = Drive(amplitude=ConstantWaveform(10.0, 1.5))

Drive with time-varying amplitude and detuning

amp = RampWaveform(5.0, 0.0, 2.0) det = ConstantWaveform(5.0, -1.0) drive = Drive(amplitude=amp, detuning=det, phase=0.5)

Attributes:

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 ConstantWaveform, RampWaveform
        >>>
        >>> # Simple constant drive
        >>> drive = Drive(amplitude=ConstantWaveform(10.0, 1.5))
        >>>
        >>> # Drive with time-varying amplitude and detuning
        >>> amp = RampWaveform(5.0, 0.0, 2.0)
        >>> det = ConstantWaveform(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 DelayWaveform(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, DelayWaveform(extra_duration))
    elif self._detuning.duration > self._amplitude.duration:
        extra_duration = self._detuning.duration - self._amplitude.duration
        self._amplitude = CompositeWaveform(self._amplitude, DelayWaveform(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

amplitude property

amplitude: Waveform

The amplitude waveform in the drive.

detuning property

detuning: Waveform

The detuning waveform in the drive.

dmm property

Detuning Map Modulator (DMM) applied to individual qubits.

phase property

phase: float

The phase value in the drive.

InterpolatedWaveform

InterpolatedWaveform(
    duration: float,
    values: ArrayLike,
    times: ArrayLike | None = None,
)

A waveform created from shape-preserving interpolation of data points.

This class creates a smooth waveform by interpolating between specified data points using PCHIP (Piecewise Cubic Hermite Interpolating Polynomial) interpolation. The interpolating curve preserves the shape of the input data: bounds (avoiding under/overshooting), monotonicity, and convexity.

Uses scipy's PchipInterpolator for the interpolation.

Attributes:

  • duration (float) –

    The waveform duration.

  • values (float) –

    Array-like sequence of waveform values at the interpolation points. Must be convertible to float. These values define the amplitude of the waveform at the corresponding time points.

  • times (float) –

    Optional array-like sequence of fractional times in the range [0, 1] indicating where to place each value on the time axis. Must have the same length as values. If not provided, values are distributed evenly across the waveform duration. Default is None.

ValueError: If times contains values outside [0, 1] or if times and values have different lengths.

Example

Create a waveform with 4 points over 100ns

values = [0.0, 1.0, 0.5, 0.0] wf = InterpolatedWaveform(100, values)

Create with custom timing

times = [0.0, 0.2, 0.8, 1.0] # Non-uniform spacing wf = InterpolatedWaveform(100, values, times)

Parameters:

  • duration (float) –

    The total duration of the waveform. Must be positive.

  • values (ArrayLike) –

    Array-like sequence of waveform values at interpolation points. Can be a list, tuple, numpy array, or any sequence convertible to float.

  • times (ArrayLike | None, default: None ) –

    Optional array-like sequence of fractional times in [0, 1]. If provided, must have the same length as values. If None, values are evenly spaced across the duration. Default is None.

Raises:

  • ValueError

    If any value in times is outside [0, 1], or if times and values have different lengths.

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • __rshift__

    Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/waveforms.py
def __init__(
    self,
    duration: float,
    values: ArrayLike,
    times: ArrayLike | None = None,
):
    """Initialize an Interpolated waveform.

    Args:
        duration: The total duration of the waveform. Must be positive.
        values: Array-like sequence of waveform values at interpolation points.
            Can be a list, tuple, numpy array, or any sequence convertible to float.
        times: Optional array-like sequence of fractional times in [0, 1]. If provided,
            must have the same length as `values`. If None, values are evenly spaced
            across the duration. Default is None.

    Raises:
        ValueError: If any value in `times` is outside [0, 1], or if `times` and
            `values` have different lengths.
    """
    super().__init__(duration)
    self._values = np.array(values, dtype=float)
    if times is not None:
        self._times = np.array(times, dtype=float)
        if any([(ft < 0) or (ft > 1) for ft in self._times]):
            raise ValueError("All values in `times` must be in [0,1].")
        if len(self._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))

    self._interp_func = PchipInterpolator(duration * self._times, values)

duration property

duration: float

Returns the duration of the waveform.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

__rshift__

__rshift__(other: Waveform) -> CompositeWaveform

Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/base_waveforms.py
def __rshift__(self, other: Waveform) -> CompositeWaveform:
    """Returns a new CompositeWaveform composed of this waveform and another."""
    if isinstance(other, Waveform):
        if isinstance(other, CompositeWaveform):
            return CompositeWaveform(self, *other._waveforms)
        return CompositeWaveform(self, other)
    else:
        raise NotImplementedError(f"Composing with object of type {type(other)} not supported.")

MockDevice

MockDevice()

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)

specs property

specs: dict[str, float | None]

Return the device specification constraints.

from_connection classmethod

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

Parameters:

  • connection (RemoteConnection) –

    connection object to fetch the available devices.

  • name (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 pasqal_cloud import PasqalCloudConnection
fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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 pasqal_cloud import PasqalCloudConnection
    fresnel_device = Device.from_connection(connection=PasqalCloudConnection(), 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)

info

info() -> None

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)

reset_converter

reset_converter() -> None

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

set_distance_unit

set_distance_unit(distance: float) -> None

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)

set_energy_unit

set_energy_unit(energy: float) -> None

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)

PiecewiseLinearWaveform

PiecewiseLinearWaveform(
    durations: list[float] | tuple[float, ...] | ndarray,
    values: list[float] | tuple[float, ...] | ndarray,
)

A piecewise linear waveform.

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

Parameters:

  • durations (list[float] | tuple[float, ...] | ndarray) –

    list or tuple of N duration values.

  • values (list[float] | tuple[float, ...] | ndarray) –

    list or tuple of N+1 waveform values.

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • max

    Get the maximum value of the waveform.

  • min

    Get the 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[float] | tuple[float, ...] | np.ndarray,
    values: list[float] | tuple[float, ...] | np.ndarray,
) -> None:

    if len(durations) + 1 != len(values) or len(durations) == 1:
        raise ValueError(
            "A PiecewiseLinearWaveform requires N durations and N + 1 values, for N >= 2."
        )

    for duration in durations:
        if duration == 0.0:
            raise ValueError("A PiecewiseLinearWaveform interval cannot have zero duration.")

    # Stored as-is (not cast to float): unlike the per-segment RampWaveforms below,
    # this attribute can retain an int dtype if `values` is int-typed.
    self.values = values

    wfs = [RampWaveform(dur, values[i], values[i + 1]) for i, dur in enumerate(durations)]

    super().__init__(*wfs)

duration property

duration: float

Returns the duration of the waveform.

durations property

durations: list[float]

Returns the list of durations of each individual waveform.

n_waveforms property

n_waveforms: int

Returns the number of waveforms.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

times property

times: list[float]

Returns the list of times when each individual waveform starts.

waveforms property

waveforms: list[Waveform]

Returns a list of the individual waveforms.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

max

max() -> float

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

min

min() -> float

Get the minimum value of the waveform.

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

QuantumProgram

QuantumProgram(register: Register, drive: Drive)

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

Parameters:

  • register (Register) –

    the register of qubits, defining their positions.

  • drive (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:
            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 property

compiled_sequence: Sequence

The Pulser sequence compiled to a specific device.

drive property

drive: Drive

The driving waveforms.

is_compiled property

is_compiled: bool

Check if the program has been compiled.

register property

register: Register

The register of qubits.

compile_to

compile_to(
    device: Device,
    profile: Literal["default", "max_energy"]
    | CompilerProfile = DEFAULT,
    device_max_duration_ratio: float | None = None,
) -> None

Compiles the quantum program for execution on a specific device.

The compilation process translates a program to make it runnable on a specific device:

  • Dimensionalization: Rescale the drive amplitude and the register positions to physical units compatible with the device.
  • Translation: Translate the program to a lower-level representation (Pulser) that can be executed on the device.

There are two compilation profiles:

  • "default": Compile the program as it is. The drive and the register positions must respect the hardware constraints of the device which can be inspected using device.specs(). The CompilerProfile.WORKING_POINT is deprecated aliases for this profile.
  • "max_energy": Scale 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.

The following option does NOT preserve the input program, but rather adapts the program to the device's constraints. Programs compiled this way are not portable across devices.

  • device_max_duration_ratio: Rescale the drive duration to a fraction of the device's maximum allowed duration. Useful in adiabatic protocols where one simply seeks to minimize the time derivative of the drive's amplitude.

Parameters:

  • device (Device) –

    The target device for compilation. Must be a QoolQit Device.

  • profile (Literal['default', 'max_energy'] | CompilerProfile, default: DEFAULT ) –

    The compilation profile used to translate the program. Defaults to CompilerProfile.DEFAULT.

  • device_max_duration_ratio (float | None, default: None ) –

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

Raises:

  • TypeError

    If device is not a QoolQit Device.

  • ValueError

    If device_max_duration_ratio is set but the device has no maximum allowed duration, or if it is not in the range (0, 1].

  • CompilationError

    If the compilation fails due to device constraints.

Source code in qoolqit/program.py
def compile_to(
    self,
    device: Device,
    profile: Literal["default", "max_energy"] | CompilerProfile = CompilerProfile.DEFAULT,
    device_max_duration_ratio: float | None = None,
) -> None:
    """Compiles the quantum program for execution on a specific device.

    The compilation process translates a program to make it runnable on a specific device:

    - Dimensionalization: Rescale the drive amplitude and the register positions to
        physical units compatible with the device.
    - Translation: Translate the program to a lower-level representation (Pulser) that
        can be executed on the device.

    There are two compilation profiles:

    - "default": Compile the program as it is. The drive and the register positions
        must respect the hardware constraints of the device which can be inspected
        using `device.specs()`. The `CompilerProfile.WORKING_POINT` is deprecated
        aliases for this profile.
    - "max_energy": Scale 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.

    The following option does NOT preserve the input program, but rather adapts the program
    to the device's constraints. Programs compiled this way are not portable across devices.

    - device_max_duration_ratio: Rescale the drive duration to a fraction of the
        device's maximum allowed duration. Useful in adiabatic protocols where one simply
        seeks to minimize the time derivative of the drive's amplitude.

    Args:
        device: The target device for compilation. Must be a QoolQit Device.
        profile: The compilation profile used to translate the program.
            Defaults to CompilerProfile.DEFAULT.
        device_max_duration_ratio: The fraction of the device's maximum allowed duration
            to set the program duration to, or None to leave it unset. Must be a number
            in the range (0, 1]. Can only be set if the device has a maximum allowed
            duration.

    Raises:
        TypeError: If `device` is not a QoolQit Device.
        ValueError: If `device_max_duration_ratio` is set but the device has no maximum
            allowed duration, or if it is not in the range (0, 1].
        CompilationError: If the compilation fails due to device constraints.
    """
    if not isinstance(device, Device):
        raise TypeError("`device` must be of type `qoolqit.devices.Device`.")

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

    profile = CompilerProfile(profile)

    compiler = SequenceCompiler(
        self.register, self.drive, device, profile, device_max_duration_ratio
    )
    self._device = device
    self._compiled_sequence = compiler.compile_sequence()

RampWaveform

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

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

Parameters:

  • duration (float) –

    the total duration.

  • initial_value (float) –

    the initial value at t = 0.

  • final_value (float) –

    the final value at t = duration.

Methods:

  • __rmul__

    Rescale this waveform by a scalar (right-hand multiplication).

  • __rshift__

    Returns a new CompositeWaveform composed of this waveform and another.

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=float(initial_value), final_value=float(final_value)
    )

duration property

duration: float

Returns the duration of the waveform.

params property

params: dict[str, float | ndarray]

Dictionary of parameters used by the waveform.

__rmul__

__rmul__(other: float) -> Waveform

Rescale this waveform by a scalar (right-hand multiplication).

Source code in qoolqit/waveforms/base_waveforms.py
def __rmul__(self, other: float) -> Waveform:
    """Rescale this waveform by a scalar (right-hand multiplication)."""
    return self.__mul__(other)

__rshift__

__rshift__(other: Waveform) -> CompositeWaveform

Returns a new CompositeWaveform composed of this waveform and another.

Source code in qoolqit/waveforms/base_waveforms.py
def __rshift__(self, other: Waveform) -> CompositeWaveform:
    """Returns a new CompositeWaveform composed of this waveform and another."""
    if isinstance(other, Waveform):
        if isinstance(other, CompositeWaveform):
            return CompositeWaveform(self, *other._waveforms)
        return CompositeWaveform(self, other)
    else:
        raise NotImplementedError(f"Composing with object of type {type(other)} not supported.")

Register

Register(
    qubits: Mapping[
        str, Sequence[float] | NDArray[float64] | Tensor
    ]
    | Mapping[
        int, Sequence[float] | NDArray[float64] | Tensor
    ],
)

A QoolQit register mapping qubit IDs to 2D coordinates.

Examples:

From a dictionary of qubit IDs and coordinates:

>>> reg = Register({"a": (0.0, 0.0), "b": (1.0, 0.0), "c": (0.0, 1.0)})
>>> reg = Register({0: (0.0, 0.0), 1: (1.0, 0.0), 2: (0.0, 1.0)})

From a list of coordinates (qubit IDs are assigned automatically as strings "0", "1", ...):

>>> reg = Register.from_coordinates([(0.0, 0.0), (1.0, 0.0), (0.0, 1.0)])

Using numpy arrays as coordinates:

>>> import numpy as np
>>> reg = Register({"a": np.array([0.0, 0.0]), "b": np.array([1.0, 0.0])})

Using torch tensors as coordinates:

>>> import torch
>>> reg = Register({"a": torch.tensor([0.0, 0.0]), "b": torch.tensor([1.0, 0.0])})

Parameters:

  • qubits (Mapping[str, Sequence[float] | NDArray[float64] | Tensor] | Mapping[int, Sequence[float] | NDArray[float64] | Tensor]) –

    a dictionary of qubits and respective 2D coordinates {q: (x, y), ...}. Each coordinate must be castable to a numpy or torch array of shape (2,).

Raises:

  • TypeError

    If qubits is not a Mapping.

  • ValueError

    If qubits dictionary is empty.

  • ValueError

    If a qubit coordinate cannot be converted to an array of floats, or if the converted coordinate is not a point in 2D.

Methods:

  • circle

    Initializes a Register with qubits arranged in a circle.

  • distances

    Distance between each qubit pair.

  • draw

    Draw the register.

  • from_coordinates

    Initializes a Register from a sequence or array of coordinates.

  • from_graph

    Initializes a Register from a graph that has coordinates.

  • interaction_matrix

    Interaction 1/r^6 between each qubit pair, as a matrix (0 on the diagonal).

  • interactions

    Interaction 1/r^6 between each qubit pair.

  • line

    Initializes a Register with qubits arranged in a line.

  • max_radial_distance

    Maximum radial distance between all qubits.

  • min_distance

    Minimum distance between all qubit pairs.

  • radial_distances

    Radial distance of each qubit from the origin.

  • rectangular

    Initializes a rectangular Register of qubits.

  • square

    Initializes a square Register of qubits.

  • triangular

    Initializes a triangular lattice Register of qubits.

Attributes:

  • n_qubits (int) –

    Number of qubits in the Register.

  • qubits (dict) –

    Returns a dictionary of qubits and respective coordinates.

  • qubits_ids (tuple[str | int, ...]) –

    Returns the qubit IDs.

Source code in qoolqit/register.py
def __init__(
    self,
    qubits: (
        Mapping[str, Sequence[float] | npt.NDArray[np.float64] | torch.Tensor]
        | Mapping[int, Sequence[float] | npt.NDArray[np.float64] | torch.Tensor]
    ),
) -> None:
    """Default constructor for the Register.

    Args:
        qubits: a dictionary of qubits and respective 2D coordinates {q: (x, y), ...}.
            Each coordinate must be castable to a numpy or torch array of shape (2,).

    Raises:
        TypeError: If `qubits` is not a Mapping.
        ValueError: If `qubits` dictionary is empty.
        ValueError: If a qubit coordinate cannot be converted to an array of
            floats, or if the converted coordinate is not a point in 2D.
    """
    if not isinstance(qubits, Mapping):
        raise TypeError("`qubits` must be a Mapping of qubit ids to coordinates.")
    if not qubits:
        raise ValueError("Register cannot be empty.")

    self._qubits_ids: tuple[str | int, ...] = tuple(qubits.keys())
    validated_coords = [self._validate_coord(k, c) for k, c in qubits.items()]
    self._coords = self._stack_coords(validated_coords)

n_qubits property

n_qubits: int

Number of qubits in the Register.

qubits property

qubits: dict

Returns a dictionary of qubits and respective coordinates.

qubits_ids property

qubits_ids: tuple[str | int, ...]

Returns the qubit IDs.

circle classmethod

circle(n: int, spacing: float = 1.0) -> Register

Initializes a Register with qubits arranged in a circle.

Parameters:

  • n (int) –

    number of qubits to place in the circle.

  • spacing (float, default: 1.0 ) –

    distance between adjacent qubits. Defaults to 1.0.

Source code in qoolqit/register.py
@classmethod
def circle(cls, n: int, spacing: float = 1.0) -> Register:
    """Initializes a Register with qubits arranged in a circle.

    Args:
        n: number of qubits to place in the circle.
        spacing: distance between adjacent qubits. Defaults to 1.0.
    """
    if n < 1:
        raise ValueError("Number of qubits must be at least 1.")
    if spacing <= 0:
        raise ValueError("Spacing must be positive.")
    if n == 1:
        return cls.from_coordinates([(0.0, 0.0)])

    step = 2.0 * math.pi / n
    r = spacing / (2.0 * math.sin(math.pi / n))
    coords = [(math.cos(step * i) * r, math.sin(step * i) * r) for i in range(n)]

    return cls.from_coordinates(coords)

distances

distances() -> dict

Distance between each qubit pair.

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

draw

draw(
    ax: Axes | None = None,
    marker_size: int = 100,
    node_color: str = "tab:green",
) -> None

Draw the register.

Parameters:

  • ax (Axes | None, default: None ) –

    an optional matplotlib Axes instance to draw on. If None, a new Axes will be created.

  • marker_size (int, default: 100 ) –

    size of the qubit markers in points squared. Defaults to 100.

  • node_color (str, default: 'tab:green' ) –

    color of the qubit markers. Defaults to "tab:green".

Source code in qoolqit/register.py
def draw(
    self, ax: Axes | None = None, marker_size: int = 100, node_color: str = "tab:green"
) -> None:
    """Draw the register.

    Args:
        ax: an optional matplotlib Axes instance to draw on.
            If None, a new Axes will be created.
        marker_size: size of the qubit markers in points squared. Defaults to 100.
        node_color: color of the qubit markers. Defaults to "tab:green".
    """
    if ax is None:
        _, ax = plt.subplots()

    marker_radius = marker_size**0.5 / 2  # in points
    annotation_offset = 1.5 * marker_radius  # place label just outside the marker

    coords = self._coords.detach().cpu().numpy() if _is_torch(self._coords) else self._coords
    for xi, yi, qid in zip(coords[:, 0], coords[:, 1], self.qubits_ids):
        ax.scatter(xi, yi, s=marker_size, color=node_color)
        ax.annotate(
            str(qid),
            xy=(xi, yi),
            xytext=(annotation_offset, annotation_offset),
            textcoords="offset points",
            ha="center",
            va="center",
        )

    ax.grid(True, color="lightgray", linestyle="--", linewidth=0.7)
    ax.set_axisbelow(True)
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.margins(0.1)

from_coordinates classmethod

from_coordinates(
    coords: Sequence[
        Sequence[float] | NDArray[float64] | Tensor
    ]
    | NDArray[float64]
    | Tensor,
) -> Register

Initializes a Register from a sequence or array of coordinates.

Qubit IDs are assigned as integers 0,1,...,N-1, where N is the number of coordinates.

Parameters:

  • coords (Sequence[Sequence[float] | NDArray[float64] | Tensor] | NDArray[float64] | Tensor) –

    a sequence of 2D coordinates, i.e. [(x, y), ...]. Each coordinate must be castable to a numpy or torch array of shape (2,). If coords is a numpy array or a torch tensor, it must be 2D and of shape (N, 2).

Raises:

  • TypeError

    If coords is a Mapping.

Source code in qoolqit/register.py
@classmethod
def from_coordinates(
    cls,
    coords: (
        Sequence[Sequence[float] | npt.NDArray[np.float64] | torch.Tensor]
        | npt.NDArray[np.float64]
        | torch.Tensor
    ),
) -> Register:
    """Initializes a Register from a sequence or array of coordinates.

    Qubit IDs are assigned as integers 0,1,...,N-1, where N is the number of coordinates.

    Args:
        coords: a sequence of 2D coordinates, i.e. [(x, y), ...].
            Each coordinate must be castable to a numpy or torch array of shape (2,).
            If `coords` is a numpy array or a torch tensor, it must be 2D and of shape (N, 2).

    Raises:
        TypeError: If `coords` is a Mapping.
    """
    if isinstance(coords, Mapping):
        raise TypeError(
            "Register.from_coordinates expects a sequence of coordinates [(x, y), ...]; "
            "pass an id-to-coordinate mapping to Register(...) directly."
        )
    coords_dict = {i: pos for i, pos in enumerate(coords)}
    return cls(coords_dict)

from_graph classmethod

from_graph(graph: DataGraph) -> Register

Initializes a Register from a graph that has coordinates.

Parameters:

  • graph (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.

    Args:
        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_matrix

interaction_matrix() -> NDArray[float64] | Tensor

Interaction 1/r^6 between each qubit pair, as a matrix (0 on the diagonal).

Source code in qoolqit/register.py
def interaction_matrix(self) -> npt.NDArray[np.float64] | torch.Tensor:
    """Interaction 1/r^6 between each qubit pair, as a matrix (0 on the diagonal)."""
    dist_matrix = _pdist(self._coords)
    interactions = _fill_diagonal(dist_matrix, 1.0)
    interactions **= -6
    return _fill_diagonal(interactions, 0.0)

interactions

interactions() -> dict

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

line classmethod

line(n: int, spacing: float = 1.0) -> Register

Initializes a Register with qubits arranged in a line.

Parameters:

  • n (int) –

    number of qubits to place in the line.

  • spacing (float, default: 1.0 ) –

    distance between adjacent qubits. Defaults to 1.0.

Source code in qoolqit/register.py
@classmethod
def line(cls, n: int, spacing: float = 1.0) -> Register:
    """Initializes a Register with qubits arranged in a line.

    Args:
        n: number of qubits to place in the line.
        spacing: distance between adjacent qubits. Defaults to 1.0.
    """
    return cls.rectangular(n, 1, row_spacing=spacing)

max_radial_distance

max_radial_distance() -> float

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

min_distance

min_distance() -> float

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_distances

radial_distances() -> dict

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 {qid: _norm(coord) for qid, coord in zip(self.qubits_ids, self._coords)}

rectangular classmethod

rectangular(
    rows: int,
    cols: int,
    row_spacing: float = 1.0,
    col_spacing: float = 1.0,
) -> Register

Initializes a rectangular Register of qubits.

Parameters:

  • rows (int) –

    number of rows in the rectangle.

  • cols (int) –

    number of columns in the rectangle.

  • row_spacing (float, default: 1.0 ) –

    distance between adjacent qubits in the row direction. Defaults to 1.0.

  • col_spacing (float, default: 1.0 ) –

    distance between adjacent qubits in the column direction. Defaults to 1.0.

Source code in qoolqit/register.py
@classmethod
def rectangular(
    cls, rows: int, cols: int, row_spacing: float = 1.0, col_spacing: float = 1.0
) -> Register:
    """Initializes a rectangular Register of qubits.

    Args:
        rows: number of rows in the rectangle.
        cols: number of columns in the rectangle.
        row_spacing: distance between adjacent qubits in the row direction. Defaults to 1.0.
        col_spacing: distance between adjacent qubits in the column direction. Defaults to 1.0.
    """
    if rows < 1 or cols < 1:
        raise ValueError("Number of rows and columns must be at least 1.")
    if row_spacing <= 0 or col_spacing <= 0:
        raise ValueError("Spacing must be positive.")

    x_offset = (rows - 1) * row_spacing / 2.0
    y_offset = (cols - 1) * col_spacing / 2.0
    coords = [
        (i * row_spacing - x_offset, j * col_spacing - y_offset)
        for i in range(rows)
        for j in range(cols)
    ]

    return cls.from_coordinates(coords)

square classmethod

square(n: int, spacing: float = 1.0) -> Register

Initializes a square Register of qubits.

Parameters:

  • n (int) –

    number of qubits along each side of the square.

  • spacing (float, default: 1.0 ) –

    distance between adjacent qubits. Defaults to 1.0.

Source code in qoolqit/register.py
@classmethod
def square(cls, n: int, spacing: float = 1.0) -> Register:
    """Initializes a square Register of qubits.

    Args:
        n: number of qubits along each side of the square.
        spacing: distance between adjacent qubits. Defaults to 1.0.
    """
    return cls.rectangular(n, n, row_spacing=spacing, col_spacing=spacing)

triangular classmethod

triangular(
    rows: int, atoms_per_row: int, spacing: float = 1.0
) -> Register

Initializes a triangular lattice Register of qubits.

Parameters:

  • rows (int) –

    number of rows in the lattice.

  • atoms_per_row (int) –

    number of qubits per row.

  • spacing (float, default: 1.0 ) –

    distance between adjacent qubits. Defaults to 1.0.

Source code in qoolqit/register.py
@classmethod
def triangular(cls, rows: int, atoms_per_row: int, spacing: float = 1.0) -> Register:
    """Initializes a triangular lattice Register of qubits.

    Args:
        rows: number of rows in the lattice.
        atoms_per_row: number of qubits per row.
        spacing: distance between adjacent qubits. Defaults to 1.0.
    """
    if rows < 1 or atoms_per_row < 1:
        raise ValueError("Number of rows and atoms per row must be at least 1.")
    if spacing <= 0:
        raise ValueError("Spacing must be positive.")

    height = math.sqrt(3.0) / 2.0
    x_offset = ((atoms_per_row - 1) / 2.0 + 0.5 * (rows // 2) / rows) * spacing
    y_offset = (rows - 1) * height * spacing / 2.0
    coords = [
        ((i + 0.5 * (j % 2)) * spacing - x_offset, j * height * spacing - y_offset)
        for j in range(rows)
        for i in range(atoms_per_row)
    ]
    return cls.from_coordinates(coords)

SequenceCompiler

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

    the QoolQit Register.

  • drive (Drive) –

    the QoolQit Drive.

  • device (Device) –

    the QoolQit Device.

  • profile (CompilerProfile) –

    the CompilerProfile to use.

  • device_max_duration_ratio (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

available_default_devices

available_default_devices() -> None

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(), AnalogDeviceWithDMM(), MockDevice()):
        dev.info()