Skip to content

Commit

Permalink
Tutorial for the abstract representation of a sequence, adding serial…
Browse files Browse the repository at this point in the history
…ization with json.dumps options (#515)

* json dumps options & ntbk without param

* Adding parametrized serialization

* Taking into account review comments

* Taking into account review comments

* deleting sampler from imports

* Deleting empty cell
  • Loading branch information
a-corni authored May 4, 2023
1 parent fb6f7d1 commit d43dccb
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 45 deletions.
10 changes: 8 additions & 2 deletions pulser-core/pulser/json/abstract_repr/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,19 @@ def abstract_repr(name: str, *args: Any, **kwargs: Any) -> dict[str, Any]:


def serialize_abstract_sequence(
seq: Sequence, seq_name: str = "pulser-exported", **defaults: Any
seq: Sequence,
seq_name: str = "pulser-exported",
json_dumps_options: dict[str, Any] = {},
**defaults: Any,
) -> str:
"""Serializes the Sequence into an abstract JSON object.
Keyword Args:
seq_name (str): A name for the sequence. If not defined, defaults
to "pulser-exported".
json_dumps_options: A mapping between optional parameters of
``json.dumps()`` (as string) and their value (parameter cannot
be "cls").
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.
Expand Down Expand Up @@ -286,4 +292,4 @@ def get_all_args(
else:
raise AbstractReprError(f"Unknown call '{call.name}'.")

return json.dumps(res, cls=AbstractReprEncoder)
return json.dumps(res, cls=AbstractReprEncoder, **json_dumps_options)
14 changes: 11 additions & 3 deletions pulser-core/pulser/sequence/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ def declare_variable(
To avoid confusion, it is recommended to store the returned
Variable instance in a Python variable with the same name.
"""
if name in ("qubits", "seq_name"):
if name in ("qubits", "seq_name", "json_dumps_options"):
raise ValueError(
f"'{name}' is a protected name. Please choose a different name"
" for the variable."
Expand Down Expand Up @@ -1232,13 +1232,19 @@ def serialize(self, **kwargs: Any) -> str:
return json.dumps(self, cls=PulserEncoder, **kwargs)

def to_abstract_repr(
self, seq_name: str = "pulser-exported", **defaults: Any
self,
seq_name: str = "pulser-exported",
json_dumps_options: dict[str, Any] = {},
**defaults: Any,
) -> str:
"""Serializes the Sequence into an abstract JSON object.
Keyword Args:
seq_name (str): A name for the sequence. If not defined, defaults
to "pulser-exported".
json_dumps_options: A mapping between optional parameters of
``json.dumps()`` (as string) and their value (parameter cannot
be "cls").
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.
Expand All @@ -1255,7 +1261,9 @@ def to_abstract_repr(
See Also:
``serialize``
"""
return serialize_abstract_sequence(self, seq_name, **defaults)
return serialize_abstract_sequence(
self, seq_name, json_dumps_options, **defaults
)

@staticmethod
def deserialize(obj: str, **kwargs: Any) -> Sequence:
Expand Down
1 change: 1 addition & 0 deletions tests/test_abstract_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,7 @@ def _check_roundtrip(serialized_seq: dict[str, Any]):
rs = seq.to_abstract_repr(
seq_name=serialized_seq["name"],
qubits=qubits_default or None,
json_dumps_options={"indent": None},
**defaults,
)
assert s == json.loads(rs)
Expand Down
65 changes: 25 additions & 40 deletions tutorials/advanced_features/Serialization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"outputs": [],
"source": [
"import numpy as np\n",
"import pulser\n",
"from pulser import Pulse, Sequence, Register\n",
"from pulser.waveforms import BlackmanWaveform\n",
"from pulser.devices import Chadoq2"
Expand All @@ -39,10 +38,11 @@
"reg = Register(qubits)\n",
"\n",
"seq = Sequence(reg, Chadoq2)\n",
"pulse_time = seq.declare_variable(\"pulse_time\", dtype=int)\n",
"seq.declare_channel(\"digital\", \"raman_local\", initial_target=\"control\")\n",
"seq.declare_channel(\"rydberg\", \"rydberg_local\", initial_target=\"control\")\n",
"\n",
"half_pi_wf = BlackmanWaveform(200, np.pi / 2)\n",
"half_pi_wf = BlackmanWaveform(pulse_time, area=np.pi / 2)\n",
"\n",
"ry = Pulse.ConstantDetuning(amplitude=half_pi_wf, detuning=0, phase=-np.pi / 2)\n",
"ry_dag = Pulse.ConstantDetuning(\n",
Expand All @@ -53,7 +53,7 @@
"seq.target(\"target\", \"digital\")\n",
"seq.add(ry_dag, \"digital\")\n",
"\n",
"pi_wf = BlackmanWaveform(200, np.pi)\n",
"pi_wf = BlackmanWaveform(pulse_time, np.pi)\n",
"pi_pulse = Pulse.ConstantDetuning(pi_wf, 0, 0)\n",
"\n",
"max_val = Chadoq2.rabi_from_blockade(8)\n",
Expand All @@ -70,7 +70,8 @@
"seq.align(\"digital\", \"rydberg\")\n",
"seq.add(ry, \"digital\")\n",
"seq.measure(\"digital\")\n",
"seq.draw()"
"seq1 = seq.build(pulse_time=200)\n",
"seq1.draw()"
]
},
{
Expand All @@ -81,10 +82,11 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"To serialize it, we simply call:"
"The former version of converting a `Sequence` into a JSON-formatted string was to use the `serialize` method. It is still supported, but a new method named `to_abstract_repr` should be favored to perform serialization. Let's compare both serialization methods:"
]
},
{
Expand All @@ -93,43 +95,31 @@
"metadata": {},
"outputs": [],
"source": [
"s = seq.serialize()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"which outputs the JSON-formatted string encoding the sequence. Although it is not particularly human-readable in its most compact form, let's look at the first 100 characters to check that it's actually a string:"
"s = seq.serialize(indent=1)\n",
"print(s[:350], \"...\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"metadata": {},
"outputs": [],
"source": [
"print(s[:100], \"...\")"
"s_readable = seq.to_abstract_repr(\n",
" json_dumps_options={\"indent\": 1},\n",
" seq_name=\"Sequence_with_defaults\",\n",
")\n",
"print(s_readable[:350], \"...\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"If we want to read it, we can also make it prettier by changing the indentation. This is done through one of the optional arguments of `json.dumps`, which we can specify in `Sequence.serialize()`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"print(seq.serialize(indent=1)[:100], \"...\")"
"We can note that it is possible to provide optional parameters of `json.dumps` such as `indent` to both methods. In `serialize`, they should be provided as optional arguments whereas for `to_abstract_repr` they should be defined as a dictionnary in the argument `json_dumps_options`.\n",
"\n",
"Providing optional arguments to `to_abstract_repr` defines default parameters in the JSON object (like the name of the sequence `seq_name`). This does not change the `Sequence` object in itself, as will be seen in the following part about deserialization."
]
},
{
Expand All @@ -140,10 +130,11 @@
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"More relevantly, the string `s` contains all the necessary information for recreating the original sequence elsewhere (it could, for example, be saved to a file and then imported). With the string `s`, one can recover the sequence `seq` by calling:"
"The generated strings contain all the necessary information for recreating the original sequence elsewhere (it could, for example, be saved to a file and then imported). In the case of strings obtained from `Sequence.serialize`, one could recover the sequence `seq` by calling `Sequence.deserialize`. This method is still supported. However, to recover the sequence `seq` from `s_readable` (converted into JSON using `Sequence.to_abstract_repr`), one should use `Sequence.from_abstract_repr`. "
]
},
{
Expand All @@ -153,14 +144,7 @@
"outputs": [],
"source": [
"recovered_seq = Sequence.deserialize(s)\n",
"recovered_seq.draw()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However, even though they are identical, keep in mind that `seq` and `recovered_seq` are not the same object."
"recovered_seq.build(pulse_time=200).draw()"
]
},
{
Expand All @@ -169,7 +153,8 @@
"metadata": {},
"outputs": [],
"source": [
"recovered_seq is seq"
"recovered_seq_from_readable = Sequence.from_abstract_repr(s_readable)\n",
"recovered_seq_from_readable.build(pulse_time=200).draw()"
]
}
],
Expand All @@ -189,7 +174,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
"version": "3.8.15"
}
},
"nbformat": 4,
Expand Down

0 comments on commit d43dccb

Please sign in to comment.