Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v1.1.0 #755

Merged
merged 8 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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