Skip to content

Commit

Permalink
Add missing QPU validation fields to the Device class (#682)
Browse files Browse the repository at this point in the history
* Add missing QPU validation fields to the Device class

* Delete xfails

* Fix typo
  • Loading branch information
HGSilveri authored Apr 26, 2024
1 parent 4981ca6 commit d83c876
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 14 deletions.
52 changes: 42 additions & 10 deletions pulser-core/pulser/devices/_device_datacls.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@

DIMENSIONS = Literal[2, 3]

ALWAYS_OPTIONAL_PARAMS = (
"max_sequence_duration",
"max_runs",
"dmm_objects",
"default_noise_model",
ALWAYS_OPTIONAL_PARAMS = ("max_sequence_duration", "max_runs")
OPTIONAL_IN_ABSTR_REPR = tuple(
list(ALWAYS_OPTIONAL_PARAMS)
+ [
"dmm_objects",
"default_noise_model",
"requires_layout",
"accepts_new_layouts",
]
)
PARAMS_WITH_ABSTR_REPR = ("channel_objects", "channel_ids", "dmm_objects")

Expand All @@ -61,7 +65,7 @@ class BaseDevice(ABC):
dmm_objects: The DMM subclass instances specifying each channel in the
device. They are referenced by their order in the list, with the ID
"dmm_[index in dmm_objects]".
rybderg_level: The value of the principal quantum number :math:`n`
rydberg_level: The value of the principal quantum number :math:`n`
when the Rydberg level used is of the form
:math:`|nS_{1/2}, m_j = +1/2\rangle`.
max_atom_num: Maximum number of atoms supported in an array.
Expand All @@ -83,6 +87,8 @@ class BaseDevice(ABC):
default_noise_model: An optional noise model characterizing the default
noise of the device. Can be used by emulator backends that support
noise.
requires_layout: Whether the register used in the sequence must be
created from a register layout. Only enforced in QPU execution.
"""

name: str
Expand All @@ -96,6 +102,7 @@ class BaseDevice(ABC):
max_layout_filling: float = 0.5
max_sequence_duration: int | None = None
max_runs: int | None = None
requires_layout: bool = False
reusable_channels: bool = field(default=False, init=False)
channel_ids: tuple[str, ...] | None = None
channel_objects: tuple[Channel, ...] = field(default_factory=tuple)
Expand Down Expand Up @@ -464,8 +471,8 @@ def _to_dict(self) -> dict[str, Any]:
def _to_abstract_repr(self) -> dict[str, Any]:
defaults = get_dataclass_defaults(fields(self))
params = self._params()
for p in ALWAYS_OPTIONAL_PARAMS:
if params[p] == defaults[p]:
for p in OPTIONAL_IN_ABSTR_REPR:
if p in params and params[p] == defaults[p]:
params.pop(p, None)
# Delete parameters of PARAMS_WITH_ABSTR_REPR in params
for p in PARAMS_WITH_ABSTR_REPR:
Expand Down Expand Up @@ -500,7 +507,15 @@ class Device(BaseDevice):
Attributes:
name: The name of the device.
dimensions: Whether it supports 2D or 3D arrays.
rybderg_level : The value of the principal quantum number :math:`n`
channel_objects: The Channel subclass instances specifying each
channel in the device.
channel_ids: Custom IDs for each channel object. When defined,
an ID must be given for each channel. If not defined, the IDs are
generated internally based on the channels' names and addressing.
dmm_objects: The DMM subclass instances specifying each channel in the
device. They are referenced by their order in the list, with the ID
"dmm_[index in dmm_objects]".
rydberg_level: The value of the principal quantum number :math:`n`
when the Rydberg level used is of the form
:math:`|nS_{1/2}, m_j = +1/2\rangle`.
max_atom_num: Maximum number of atoms supported in an array.
Expand All @@ -522,15 +537,22 @@ class Device(BaseDevice):
default_noise_model: An optional noise model characterizing the default
noise of the device. Can be used by emulator backends that support
noise.
requires_layout: Whether the register used in the sequence must be
created from a register layout. Only enforced in QPU execution.
pre_calibrated_layouts: RegisterLayout instances that are already
available on the Device.
accepts_new_layouts: Whether registers built from register layouts
that are not already calibrated are accepted. Only enforced in
QPU execution.
"""

max_atom_num: int
max_radial_distance: int
requires_layout: bool = True
pre_calibrated_layouts: tuple[RegisterLayout, ...] = field(
default_factory=tuple
)
accepts_new_layouts: bool = True

def __post_init__(self) -> None:
super().__post_init__()
Expand Down Expand Up @@ -701,7 +723,15 @@ class VirtualDevice(BaseDevice):
Attributes:
name: The name of the device.
dimensions: Whether it supports 2D or 3D arrays.
rybderg_level : The value of the principal quantum number :math:`n`
channel_objects: The Channel subclass instances specifying each
channel in the device.
channel_ids: Custom IDs for each channel object. When defined,
an ID must be given for each channel. If not defined, the IDs are
generated internally based on the channels' names and addressing.
dmm_objects: The DMM subclass instances specifying each channel in the
device. They are referenced by their order in the list, with the ID
"dmm_[index in dmm_objects]".
rydberg_level: The value of the principal quantum number :math:`n`
when the Rydberg level used is of the form
:math:`|nS_{1/2}, m_j = +1/2\rangle`.
max_atom_num: Maximum number of atoms supported in an array.
Expand All @@ -723,6 +753,8 @@ class VirtualDevice(BaseDevice):
default_noise_model: An optional noise model characterizing the default
noise of the device. Can be used by emulator backends that support
noise.
requires_layout: Whether the register used in the sequence must be
created from a register layout. Only enforced in QPU execution.
reusable_channels: Whether each channel can be declared multiple times
on the same pulse sequence.
"""
Expand Down
15 changes: 12 additions & 3 deletions tests/test_abstract_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,18 @@ def check_error_raised(
)
assert isinstance(prev_err.__cause__, ValueError)

@pytest.mark.parametrize("field", ["max_sequence_duration", "max_runs"])
def test_optional_device_fields(self, field):
device = replace(MockDevice, **{field: 1000})
@pytest.mark.parametrize(
"og_device, field, value",
[
(MockDevice, "max_sequence_duration", 1000),
(MockDevice, "max_runs", 100),
(MockDevice, "requires_layout", True),
(AnalogDevice, "requires_layout", False),
(AnalogDevice, "accepts_new_layouts", False),
],
)
def test_optional_device_fields(self, og_device, field, value):
device = replace(og_device, **{field: value})
dev_str = device.to_abstract_repr()
assert device == deserialize_device(dev_str)

Expand Down
4 changes: 3 additions & 1 deletion tests/test_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,7 @@ def test_convert_to_virtual():
).to_virtual() == VirtualDevice(
supports_slm_mask=False,
reusable_channels=False,
requires_layout=True,
dmm_objects=(),
**params,
)
Expand All @@ -434,7 +435,8 @@ def test_device_params():
init_virtual_params = virtual_DigitalAnalogDevice._params(init_only=True)
assert all_virtual_params == init_virtual_params
assert set(all_params) - set(all_virtual_params) == {
"pre_calibrated_layouts"
"pre_calibrated_layouts",
"accepts_new_layouts",
}


Expand Down

0 comments on commit d83c876

Please sign in to comment.