From a410687f47681572498cea0ee76ef19ec89c2092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:30:31 +0200 Subject: [PATCH] Optionally skip JSON schema validation after serialization (#693) * Optionally skip JSON schema validation after serialization * Improving docstring description * Relax precision in UT --- .../pulser/json/abstract_repr/serializer.py | 11 ++++++++++- pulser-core/pulser/sequence/sequence.py | 14 +++++++++++++- tests/test_abstract_repr.py | 11 +++++++++++ tests/test_sequence_sampler.py | 4 ++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/pulser-core/pulser/json/abstract_repr/serializer.py b/pulser-core/pulser/json/abstract_repr/serializer.py index 2ddac0cf..5ebf2cd3 100644 --- a/pulser-core/pulser/json/abstract_repr/serializer.py +++ b/pulser-core/pulser/json/abstract_repr/serializer.py @@ -104,6 +104,7 @@ def serialize_abstract_sequence( seq: Sequence, seq_name: str = "pulser-exported", json_dumps_options: dict[str, Any] = {}, + skip_validation: bool = False, **defaults: Any, ) -> str: """Serializes the Sequence into an abstract JSON object. @@ -114,6 +115,13 @@ def serialize_abstract_sequence( json_dumps_options: A mapping between optional parameters of ``json.dumps()`` (as string) and their value (parameter cannot be "cls"). + skip_validation: Whether to skip the validation of the serialized + sequence against the abstract representation's JSON schema. + Skipping the validation is useful to cut down on execution + time, as this step takes significantly longer than the + serialization itself; it is also low risk, as the validation + is only defensively checking that there are no bugs in the + serialized sequence. defaults: The default values for all the variables declared in this Sequence instance, indexed by the name given upon declaration. Check ``Sequence.declared_variables`` to see all the variables. @@ -381,5 +389,6 @@ def remove_kwarg_if_default( abstr_seq_str = json.dumps( res, cls=AbstractReprEncoder, **json_dumps_options ) - validate_abstract_repr(abstr_seq_str, "sequence") + if not skip_validation: + validate_abstract_repr(abstr_seq_str, "sequence") return abstr_seq_str diff --git a/pulser-core/pulser/sequence/sequence.py b/pulser-core/pulser/sequence/sequence.py index 0d019bb0..7ae1e01b 100644 --- a/pulser-core/pulser/sequence/sequence.py +++ b/pulser-core/pulser/sequence/sequence.py @@ -1747,6 +1747,7 @@ def to_abstract_repr( self, seq_name: str = "pulser-exported", json_dumps_options: dict[str, Any] = {}, + skip_validation: bool = False, **defaults: Any, ) -> str: """Serializes the Sequence into an abstract JSON object. @@ -1757,6 +1758,13 @@ def to_abstract_repr( json_dumps_options: A mapping between optional parameters of ``json.dumps()`` (as string) and their value (parameter cannot be "cls"). + skip_validation: Whether to skip the validation of the serialized + sequence against the abstract representation's JSON schema. + Skipping the validation is useful to cut down on execution + time, as this step takes significantly longer than the + serialization itself; it is also low risk, as the validation + is only defensively checking that there are no bugs in the + serialized sequence. defaults: The default values for all the variables declared in this Sequence instance, indexed by the name given upon declaration. Check ``Sequence.declared_variables`` to see all the variables. @@ -1772,7 +1780,11 @@ def to_abstract_repr( """ try: return serialize_abstract_sequence( - self, seq_name, json_dumps_options, **defaults + self, + seq_name=seq_name, + json_dumps_options=json_dumps_options, + skip_validation=skip_validation, + **defaults, ) except jsonschema.exceptions.ValidationError as e: if self.is_parametrized(): diff --git a/tests/test_abstract_repr.py b/tests/test_abstract_repr.py index e674256d..8f42e9d3 100644 --- a/tests/test_abstract_repr.py +++ b/tests/test_abstract_repr.py @@ -1155,6 +1155,17 @@ def test_numpy_types(self): == "abc" ) + @pytest.mark.parametrize("skip_validation", [False, True]) + def test_skip_validation(self, sequence, skip_validation): + with patch( + "pulser.json.abstract_repr.serializer.validate_abstract_repr" + ) as mock: + sequence.to_abstract_repr(skip_validation=skip_validation) + if skip_validation: + mock.assert_not_called() + else: + mock.assert_called_once() + def _get_serialized_seq( operations: list[dict] = [], diff --git a/tests/test_sequence_sampler.py b/tests/test_sequence_sampler.py index 9e633a99..19cb9fd7 100644 --- a/tests/test_sequence_sampler.py +++ b/tests/test_sequence_sampler.py @@ -24,7 +24,7 @@ import pulser_simulation from pulser.channels.dmm import DMM from pulser.devices import Device, MockDevice -from pulser.pulse import Pulse +from pulser.pulse import PHASE_PRECISION, Pulse from pulser.register.mappable_reg import MappableRegister from pulser.register.register_layout import RegisterLayout from pulser.sampler import sample @@ -480,7 +480,7 @@ def test_phase_modulation(off_center): np.testing.assert_allclose( seq_samples.phase_modulation + 2 * np.pi * off_center, full_phase.samples, - atol=1e-15, + atol=PHASE_PRECISION, )