Skip to content

Commit

Permalink
Release v1.1.0
Browse files Browse the repository at this point in the history
**Main changes**:
- Add layout restricting parameters to BaseDevice (#751) 
- Incorporate automatic layout generation in Register (#753) 

**Fixes**:
- Fix rounding error in RampWaveform (#747) 
- Fix normalisation in ring of atoms (#750)
- Improve the NoiseModel unused parameters warning message (#752)
  • Loading branch information
HGSilveri authored Oct 18, 2024
2 parents 99842f0 + c0173a7 commit 14da4eb
Show file tree
Hide file tree
Showing 21 changed files with 507 additions and 12 deletions.
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.0
1.1.0
79 changes: 78 additions & 1 deletion pulser-core/pulser/devices/_device_datacls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,20 @@

DIMENSIONS = Literal[2, 3]

ALWAYS_OPTIONAL_PARAMS = ("max_sequence_duration", "max_runs")
ALWAYS_OPTIONAL_PARAMS = (
"max_sequence_duration",
"max_runs",
"optimal_layout_filling",
"max_layout_traps",
)
OPTIONAL_IN_ABSTR_REPR = tuple(
list(ALWAYS_OPTIONAL_PARAMS)
+ [
"dmm_objects",
"default_noise_model",
"requires_layout",
"accepts_new_layouts",
"min_layout_traps",
]
)
PARAMS_WITH_ABSTR_REPR = ("channel_objects", "channel_ids", "dmm_objects")
Expand Down Expand Up @@ -83,6 +89,11 @@ class BaseDevice(ABC):
supports_slm_mask: Whether the device supports the SLM mask feature.
max_layout_filling: The largest fraction of a layout that can be filled
with atoms.
optimal_layout_filling: An optional value for the fraction of a layout
that should be filled with atoms.
min_layout_traps: The minimum number of traps a layout can have.
max_layout_traps: An optional value for the maximum number of traps a
layout can have.
max_sequence_duration: The maximum allowed duration for a sequence
(in ns).
max_runs: The maximum number of runs allowed on the device. Only used
Expand All @@ -103,6 +114,9 @@ class BaseDevice(ABC):
interaction_coeff_xy: float | None = None
supports_slm_mask: bool = False
max_layout_filling: float = 0.5
optimal_layout_filling: float | None = None
min_layout_traps: int = 1
max_layout_traps: int | None = None
max_sequence_duration: int | None = None
max_runs: int | None = None
requires_layout: bool = False
Expand Down Expand Up @@ -141,6 +155,8 @@ def type_check(
"max_radial_distance",
"max_sequence_duration",
"max_runs",
"min_layout_traps",
"max_layout_traps",
):
value = getattr(self, param)
if (
Expand Down Expand Up @@ -180,6 +196,40 @@ def type_check(
f"not {self.max_layout_filling}."
)

if self.optimal_layout_filling is not None and not (
0.0 < self.optimal_layout_filling <= self.max_layout_filling
):
raise ValueError(
"When defined, the optimal layout filling fraction "
"must be greater than 0. and less than or equal to "
f"`max_layout_filling` ({self.max_layout_filling}), "
f"not {self.optimal_layout_filling}."
)

if self.max_layout_traps is not None:
if self.max_layout_traps < self.min_layout_traps:
raise ValueError(
"The maximum number of layout traps "
f"({self.max_layout_traps}) must be greater than "
"or equal to the minimum number of layout traps "
f"({self.min_layout_traps})."
)
if (
self.max_atom_num is not None
and (
max_atoms_ := int(
self.max_layout_filling * self.max_layout_traps
)
)
< self.max_atom_num
):
raise ValueError(
"With the given maximum layout filling and maximum number "
f"of traps, a layout supports at most {max_atoms_} atoms, "
"which is less than the maximum number of atoms allowed"
f"({self.max_atom_num})."
)

for ch_obj in self.channel_objects:
type_check("All channels", Channel, value_override=ch_obj)

Expand Down Expand Up @@ -360,6 +410,23 @@ def validate_layout(self, layout: RegisterLayout) -> None:
f"{self.dimensions} dimensions."
)

if layout.number_of_traps < self.min_layout_traps:
raise ValueError(
"The device requires register layouts to have "
f"at least {self.min_layout_traps} traps; "
f"{layout!s} has only {layout.number_of_traps}."
)

if (
self.max_layout_traps is not None
and layout.number_of_traps > self.max_layout_traps
):
raise ValueError(
"The device requires register layouts to have "
f"at most {self.max_layout_traps} traps; "
f"{layout!s} has {layout.number_of_traps}."
)

self._validate_coords(layout.traps_dict, kind="traps")

def validate_layout_filling(
Expand Down Expand Up @@ -547,6 +614,11 @@ class Device(BaseDevice):
supports_slm_mask: Whether the device supports the SLM mask feature.
max_layout_filling: The largest fraction of a layout that can be filled
with atoms.
optimal_layout_filling: An optional value for the fraction of a layout
that should be filled with atoms.
min_layout_traps: The minimum number of traps a layout can have.
max_layout_traps: An optional value for the maximum number of traps a
layout can have.
max_sequence_duration: The maximum allowed duration for a sequence
(in ns).
max_runs: The maximum number of runs allowed on the device. Only used
Expand Down Expand Up @@ -792,6 +864,11 @@ class VirtualDevice(BaseDevice):
supports_slm_mask: Whether the device supports the SLM mask feature.
max_layout_filling: The largest fraction of a layout that can be filled
with atoms.
optimal_layout_filling: An optional value for the fraction of a layout
that should be filled with atoms.
min_layout_traps: The minimum number of traps a layout can have.
max_layout_traps: An optional value for the maximum number of traps a
layout can have.
max_sequence_duration: The maximum allowed duration for a sequence
(in ns).
max_runs: The maximum number of runs allowed on the device. Only used
Expand Down
32 changes: 32 additions & 0 deletions pulser-core/pulser/json/abstract_repr/schemas/device-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@
"description": "The largest fraction of a layout that can be filled with atoms.",
"type": "number"
},
"max_layout_traps": {
"description": "The maximum number of traps a layout can have.",
"type": "number"
},
"max_radial_distance": {
"description": "Maximum distance an atom can be from the center of the array (in µm).",
"type": "number"
Expand All @@ -182,17 +186,29 @@
"description": "The closest together two atoms can be (in μm).",
"type": "number"
},
"min_layout_traps": {
"description": "The minimum number of traps a layout can have.",
"type": "number"
},
"name": {
"description": "A unique name for the device.",
"type": "string"
},
"optimal_layout_filling": {
"description": "The optimal fraction of a layout that should be filled with atoms.",
"type": "number"
},
"pre_calibrated_layouts": {
"description": "Register layouts already calibrated on the device.",
"items": {
"$ref": "layout-schema.json"
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"requires_layout": {
"description": "Whether the register used in the sequence must be created from a register layout. Only enforced in QPU execution.",
"type": "boolean"
Expand Down Expand Up @@ -288,6 +304,10 @@
"description": "The largest fraction of a layout that can be filled with atoms.",
"type": "number"
},
"max_layout_traps": {
"description": "The maximum number of traps a layout can have.",
"type": "number"
},
"max_radial_distance": {
"description": "Maximum distance an atom can be from the center of the array (in µm).",
"type": [
Expand All @@ -307,10 +327,22 @@
"description": "The closest together two atoms can be (in μm).",
"type": "number"
},
"min_layout_traps": {
"description": "The minimum number of traps a layout can have.",
"type": "number"
},
"name": {
"description": "A unique name for the device.",
"type": "string"
},
"optimal_layout_filling": {
"description": "The optimal fraction of a layout that should be filled with atoms.",
"type": "number"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"requires_layout": {
"description": "Whether the register used in the sequence must be created from a register layout. Only enforced in QPU execution.",
"type": "boolean"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand All @@ -54,6 +58,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
"p_false_pos": {
"type": "number"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"relaxation_rate": {
"type": "number"
},
Expand Down
16 changes: 16 additions & 0 deletions pulser-core/pulser/json/abstract_repr/schemas/register-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand All @@ -94,6 +98,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand Down Expand Up @@ -125,6 +133,10 @@
"$ref": "#/definitions/Layout2D",
"description": "The trap layout underlying the register."
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"register": {
"description": "A 2D register containing a set of atoms.",
"items": {
Expand All @@ -145,6 +157,10 @@
"$ref": "#/definitions/Layout3D",
"description": "The trap layout underlying the register."
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"register": {
"description": "A 3D register containing a set of atoms.",
"items": {
Expand Down
20 changes: 20 additions & 0 deletions pulser-core/pulser/json/abstract_repr/schemas/sequence-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand All @@ -420,6 +424,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"slug": {
"description": "An optional name for the layout.",
"type": "string"
Expand Down Expand Up @@ -1013,6 +1021,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"register": {
"description": "A 2D register containing a set of atoms.",
"items": {
Expand Down Expand Up @@ -1110,6 +1122,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"register": {
"description": "A 3D register containing a set of atoms.",
"items": {
Expand Down Expand Up @@ -1207,6 +1223,10 @@
},
"type": "array"
},
"pulser_version": {
"description": "The pulser version used to serialize the object.",
"type": "string"
},
"register": {
"description": "A list of qubit IDs.",
"items": {
Expand Down
27 changes: 25 additions & 2 deletions pulser-core/pulser/json/abstract_repr/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
from typing import Literal

import jsonschema
from packaging.version import InvalidVersion, Version
from referencing import Registry, Resource

import pulser
from pulser.json.abstract_repr import SCHEMAS, SCHEMAS_PATH
from pulser.json.exceptions import AbstractReprError

LEGACY_JSONSCHEMA = "4.18" > version("jsonschema") >= "4.17.3"
LEGACY_JSONSCHEMA = (
Version("4.18") > Version(version("jsonschema")) >= Version("4.17.3")
)

REGISTRY: Registry = Registry(
[
Expand Down Expand Up @@ -52,4 +57,22 @@ def validate_abstract_repr(
)
else: # pragma: no cover
validate_args["registry"] = REGISTRY
jsonschema.validate(**validate_args)
try:
jsonschema.validate(**validate_args)
except Exception as exc:
try:
ser_pulser_version = Version(obj.get("pulser_version", "0.0.0"))
except InvalidVersion:
# In case the serialized version is invalid
raise exc
if Version(pulser.__version__) < ser_pulser_version:
raise AbstractReprError(
"The provided object is invalid under the current abstract "
"representation schema. It appears it was serialized with a "
f"more recent version of pulser ({ser_pulser_version!s}) than "
f"the one currently being used ({pulser.__version__}). "
"It is possible validation failed because new features have "
"since been added; consider upgrading your pulser "
"installation and retrying."
) from exc
raise exc
Loading

0 comments on commit 14da4eb

Please sign in to comment.