JSON Serialization

[1]:
import numpy as np
from pulser import Pulse, Sequence, Register
from pulser.waveforms import BlackmanWaveform
from pulser.devices import DigitalAnalogDevice

Often times, it is useful to import/export a given Sequence between different locations. To enable this, the Sequence object supports serialization and deserialization into JSON-formatted strings. This will work for any given Sequence. Take for example, this sequence that creates the Bell state \(|\Phi^+\rangle = \frac{|00\rangle + |11\rangle}{\sqrt{2}}\):

[2]:
qubits = {"control": (-2, 0), "target": (2, 0)}
reg = Register(qubits)

seq = Sequence(reg, DigitalAnalogDevice)
pulse_time = seq.declare_variable("pulse_time", dtype=int)
seq.declare_channel("digital", "raman_local", initial_target="control")
seq.declare_channel("rydberg", "rydberg_local", initial_target="control")

half_pi_wf = BlackmanWaveform(pulse_time, area=np.pi / 2)

ry = Pulse.ConstantDetuning(amplitude=half_pi_wf, detuning=0, phase=-np.pi / 2)
ry_dag = Pulse.ConstantDetuning(
    amplitude=half_pi_wf, detuning=0, phase=np.pi / 2
)

seq.add(ry, "digital")
seq.target("target", "digital")
seq.add(ry_dag, "digital")

pi_wf = BlackmanWaveform(pulse_time, np.pi)
pi_pulse = Pulse.ConstantDetuning(pi_wf, 0, 0)

max_val = DigitalAnalogDevice.rabi_from_blockade(9)
two_pi_wf = BlackmanWaveform.from_max_val(max_val, 2 * np.pi)
two_pi_pulse = Pulse.ConstantDetuning(two_pi_wf, 0, 0)

seq.align("digital", "rydberg")
seq.add(pi_pulse, "rydberg")
seq.target("target", "rydberg")
seq.add(two_pi_pulse, "rydberg")
seq.target("control", "rydberg")
seq.add(pi_pulse, "rydberg")

seq.align("digital", "rydberg")
seq.add(ry, "digital")
seq.measure("digital")
seq1 = seq.build(pulse_time=200)
seq1.draw()
../_images/tutorials_serialization_3_0.png

Serialize

To serialize, use Sequence.to_abstract_repr():

[3]:
s_readable = seq.to_abstract_repr(
    json_dumps_options={"indent": 1},
    seq_name="Sequence_with_defaults",
)
print(s_readable[:350], "...")
{
 "version": "1",
 "name": "Sequence_with_defaults",
 "register": [
  {
   "name": "control",
   "x": -2.0,
   "y": 0.0
  },
  {
   "name": "target",
   "x": 2.0,
   "y": 0.0
  }
 ],
 "channels": {
  "digital": "raman_local",
  "rydberg": "rydberg_local"
 },
 "variables": {
  "pulse_time": {
   "type": "int",
   "value": [
    0
   ]
  }
 },
 "ope ...

We can note that it is possible to provide optional parameters of json.dumps such as indent via a dictionnary in the argument json_dumps_options.

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 we’ll see in the following part about deserialization.

Deserialize

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). To recover the sequence seq from s_readable (converted into JSON using Sequence.to_abstract_repr()), one should use Sequence.from_abstract_repr().

[4]:
recovered_seq = Sequence.from_abstract_repr(s_readable)
recovered_seq.build(pulse_time=200).draw()
../_images/tutorials_serialization_10_0.png