Repository: pasqal-io/Pulser

Issue:
Serializing a Sequence
In order to send a `Sequence` over to the QPU, there will need to be some sort of serialization. Although JSON is the obvious choice (mainly because it's the only one I know...), I'm sure there are other options, which might be better suited for the job. What's your take on this, @bejito ?

Another thing to decide here is whether or not we want to deserialize back to a Pulser Sequence. Currently, the translation from Pulser to the QM program is built to take in the `Sequence` object, so that would be a point in favour of having a mechanism for deserialization. Apart from the work involved (which I would risk saying is not tremendous), do you find any points against building this?

Discussion:

Patch:
diff --git a/pulser/__init__.py b/pulser/__init__.py
index 503f9d1..f4d5844 100644
--- a/pulser/__init__.py
+++ b/pulser/__init__.py
@@ -22,6 +22,4 @@ from pulser.register import Register
 
 from pulser.sequence import Sequence
 
-from pulser.seqbuilder import SequenceBuilder
-
 from pulser.simulation import Simulation
diff --git a/pulser/_json_coders.py b/pulser/_json_coders.py
new file mode 100644
index 0000000..71403b6
--- /dev/null
+++ b/pulser/_json_coders.py
@@ -0,0 +1,92 @@
+# Copyright 2020 Pulser Development Team
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import importlib
+from json import JSONEncoder, JSONDecoder
+
+import numpy as np
+
+from pulser.utils import obj_to_dict
+from pulser.parametrized import Variable
+
+
+class PulserEncoder(JSONEncoder):
+    def default(self, o):
+        if hasattr(o, "_to_dict"):
+            return o._to_dict()
+        elif type(o) == type:
+            return obj_to_dict(o, _build=False, _name=o.__name__)
+        elif isinstance(o, np.ndarray):
+            return obj_to_dict(o, o.tolist(), _name="array")
+        elif isinstance(o, set):
+            return obj_to_dict(o, list(o))
+        else:
+            return JSONEncoder.default(self, o)
+
+
+class PulserDecoder(JSONDecoder):
+    def __init__(self, *args, **kwargs):
+        # TODO: Check version compatibility (stored at the Sequence level)
+        self.vars = {}
+        super().__init__(object_hook=self.object_hook, *args, **kwargs)
+
+    def object_hook(self, obj):
+        try:
+            build = obj["_build"]
+            obj_name = obj["__name__"]
+            module_str = obj["__module__"]
+        except KeyError:
+            return obj
+
+        if (obj_name == "Variable" and
+           module_str == "pulser.parametrized.variable"):
+            var_name = obj["name"]
+            try:
+                var = self.vars[var_name]
+                assert var.name == var_name, (f"Variable {var.name} already "
+                                              + f"declared under {var_name}.")
+                assert var.dtype == obj["dtype"], (
+                    "Mismatching variable types for variables under the name "
+                    + f"'{var_name}'."
+                    )
+                assert var.size == obj["size"], (
+                    "Mismatching sizes for variables under the name "
+                    + f"'{var_name}'."
+                    )
+            except KeyError:
+                var = Variable(var_name, obj["dtype"], obj["size"])
+                self.vars[var_name] = var
+
+            return var
+
+        module = importlib.import_module(module_str)
+        cls = getattr(module, obj_name)
+
+        if not build:
+            return cls
+
+        if obj_name == "Sequence":
+            seq = cls(*obj["__args__"], **obj["__kwargs__"])
+            for name, args, kwargs in obj["calls"]:
+                getattr(seq, name)(*args, **kwargs)
+            seq._building = obj["vars"] == {}
+            for name, var in obj["vars"].items():
+                assert name not in seq._variables, ("Multiples variables with"
+                                                    + f" the name '{name}'.")
+                seq._variables[name] = var
+            for name, args, kwargs in obj["to_build_calls"]:
+                getattr(seq, name)(*args, **kwargs)
+            return seq
+        else:
+            return cls(*obj["__args__"], **obj["__kwargs__"])
diff --git a/pulser/devices/_pasqal_device.py b/pulser/devices/_pasqal_device.py
index 6b26378..0c5cb5b 100644
--- a/pulser/devices/_pasqal_device.py
+++ b/pulser/devices/_pasqal_device.py
@@ -20,6 +20,7 @@ from scipy.spatial.distance import pdist
 
 from pulser.channels import Channel
 from pulser.register import Register
+from pulser.utils import obj_to_dict
 
 
 @dataclass(frozen=True, repr=False)
@@ -153,3 +154,7 @@ class PasqalDevice:
                 ch_lines.append(f" - '{name}': {ch!r}")
 
         return "\n".join(lines + ch_lines)
+
+    def _to_dict(self):
+        return obj_to_dict(self, _build=False, _module="pulser.devices",
+                           _name=self.name)
diff --git a/pulser/parametrized/paramabc.py b/pulser/parametrized/paramabc.py
index f661c27..72f2810 100644
--- a/pulser/parametrized/paramabc.py
+++ b/pulser/parametrized/paramabc.py
@@ -25,6 +25,11 @@ class Parametrized(ABC):
         pass
 
     @abstractmethod
-    def build():
+    def build(self):
         """Builds the object."""
         pass
+
+    @abstractmethod
+    def _to_dict(self):
+        """Serializes the object in a dictionary."""
+        pass
diff --git a/pulser/parametrized/paramobj.py b/pulser/parametrized/paramobj.py
index 557a6b0..2764f3e 100644
--- a/pulser/parametrized/paramobj.py
+++ b/pulser/parametrized/paramobj.py
@@ -18,6 +18,7 @@ import operator
 import warnings
 
 from pulser.parametrized import Parametrized
+from pulser.utils import obj_to_dict
 
 # Availabe operations on parameterized objects with OpSupport
 reversible_ops = [
@@ -97,6 +98,14 @@ class ParamObj(Parametrized, OpSupport):
             self._instance = obj(*args_, **kwargs_)
         return self._instance
 
+    def _to_dict(self):
+        if isinstance(self.cls, Parametrized):
+            cls_dict = self.cls._to_dict()
+        else:
+            cls_dict = obj_to_dict(self, _build=False, _name=self.cls.__name__,
+                                   _module=self.cls.__module__)
+        return obj_to_dict(self, cls_dict, *self.args, **self.kwargs)
+
     def __call__(self, *args, **kwargs):
         obj = ParamObj(self, *args, **kwargs)
         warnings.warn("Calls to methods of parametrized objects are only "
diff --git a/pulser/parametrized/variable.py b/pulser/parametrized/variable.py
index 25a6ece..76aa526 100644
--- a/pulser/parametrized/variable.py
+++ b/pulser/parametrized/variable.py
@@ -12,16 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from dataclasses import dataclass
+import dataclasses
 from typing import Union
 
 import numpy as np
 
 from pulser.parametrized import Parametrized
 from pulser.parametrized.paramobj import OpSupport
+from pulser.utils import obj_to_dict
 
 
-@dataclass(frozen=True, eq=False)
+@dataclasses.dataclass(frozen=True, eq=False)
 class Variable(Parametrized, OpSupport):
     """A variable for parametrized sequence building.
 
@@ -78,6 +79,11 @@ class Variable(Parametrized, OpSupport):
 
         return self.value
 
+    def _to_dict(self):
+        d = obj_to_dict(self, _build=False)
+        d.update(dataclasses.asdict(self))
+        return d
+
     def __str__(self):
         return self.name
 
@@ -96,7 +102,7 @@ class Variable(Parametrized, OpSupport):
         return _VariableItem(self, key)
 
 
-@dataclass(frozen=True)
+@dataclasses.dataclass(frozen=True)
 class _VariableItem(Parametrized, OpSupport):
     """Stores access to items of a variable with multiple values."""
 
@@ -111,6 +117,10 @@ class _VariableItem(Parametrized, OpSupport):
         """Return the variable's item(s) values."""
         return self.var.build()[self.key]
 
+    def _to_dict(self):
+        return obj_to_dict(self, self.var, self.key,
+                           _module="operator", _name="getitem")
+
     def __str__(self):
         if isinstance(self.key, slice):
             items = ["" if x is None else str(x)
diff --git a/pulser/pulse.py b/pulser/pulse.py
index 77f34ca..8aa75ed 100644
--- a/pulser/pulse.py
+++ b/pulser/pulse.py
@@ -20,6 +20,7 @@ import numpy as np
 
 from pulser.parametrized import Parametrized, ParamObj
 from pulser.waveforms import Waveform, ConstantWaveform
+from pulser.utils import obj_to_dict
 
 
 class Pulse:
@@ -131,6 +132,10 @@ class Pulse:
         fig.tight_layout()
         plt.show()
 
+    def _to_dict(self):
+        return obj_to_dict(self, self.amplitude, self.detuning, self.phase,
+                           post_phase_shift=self.post_phase_shift)
+
     def __str__(self):
         return "Pulse(Amp={!s}, Detuning={!s}, Phase={:.3g})".format(
             self.amplitude, self.detuning, self.phase)
diff --git a/pulser/register.py b/pulser/register.py
index eb24b5b..2379043 100644
--- a/pulser/register.py
+++ b/pulser/register.py
@@ -17,6 +17,8 @@ from matplotlib import collections as mc
 import numpy as np
 from scipy.spatial import KDTree
 
+from pulser.utils import obj_to_dict
+
 
 class Register:
     """A quantum register containing a set of qubits.
@@ -217,3 +219,7 @@ class Register:
             ax.axhline(0, c='grey', alpha=0.5, linestyle=':')
 
         plt.show()
+
+    def _to_dict(self):
+        qs = dict(zip(self._ids, map(np.ndarray.tolist, self._coords)))
+        return obj_to_dict(self, qs)
diff --git a/pulser/seqbuilder.py b/pulser/seqbuilder.py
deleted file mode 100644
index aa40e2a..0000000
--- a/pulser/seqbuilder.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# Copyright 2020 Pulser Development Team
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import copy
-from collections import namedtuple
-from functools import wraps
-from itertools import chain
-import warnings
-
-from pulser.parametrized import Parametrized, Variable
-from pulser.sequence import Sequence
-
-_Call = namedtuple("_Call", ['name', 'args', 'kwargs'])
-
-
-def _store(func):
-    """Stores a Sequence building call, potentially with Variable inputs."""
-    if not hasattr(Sequence, func.__name__):
-        raise AttributeError("Trying to store a call to a function that's not "
-                             "in Sequence.")
-
-    @wraps(func)
-    def wrapper(self, *args, **kwargs):
-        if hasattr(self, "_measurement"):
-            raise SystemError("The sequence has been measured, no further "
-                              "changes are allowed.")
-        # Check if all Parametrized inputs stem from declared variables
-        for x in chain(args, kwargs.values()):
-            if isinstance(x, Parametrized):
-                for name, var in x.variables.items():
-                    if name not in self._variables:
-                        raise ValueError(f"Unknown variable '{name}'.")
-                    elif self._variables[name] is not var:
-                        raise ValueError(
-                            f"{x} has variables that don't come from this "
-                            "SequenceBuilder. Use only what's returned by "
-                            "this SequenceBuilder's 'declare_variable' method"
-                            " as your variables."
-                            )
-        func(self, *args, **kwargs)
-        self._calls.append(_Call(func.__name__, args, kwargs))
-    return wrapper
-
-
-class SequenceBuilder:
-    """A blueprint for a Sequence.
-
-    The SequenceBuilder is designed to replicate the process of building a
-    regular Sequence as closely as possible, while allowing the use of
-    variable parameters. These variables have to be obtained through
-    `SequenceBuilder.declare_variable()` and their values have to be
-    specified as arguments of `SequenceBuilder.build()`, which returns the
-    corresponding Sequence.
-
-    The variables declared through a SequenceBuilder can be used to create
-    parametrized versions of Waveform and Pulse objects, which in turn can be
-    added to the SequenceBuilder. Additionally, simple arithmetic operations
-    involving variables are also supported and will return parametrized objects
-    that are functions of the involved variables.
-
-    Args:
-        register(Register): The atom register on which to apply the pulses.
-        device(PasqalDevice): A valid device in which to execute the Sequence
-            (import it from ``pulser.devices``).
-
-    Note:
-        The register, device and channel declarations do not support variable
-        parameters. As such, they are the same for all Sequences built by a
-        specific SequenceBuilder.
-    """
-    def __init__(self, register, device):
-        """Initializes a new pulse sequence."""
-        self._root = Sequence(register, device)
-        self._calls = []    # Stores the calls that make the Sequence
-        self._variables = {}
-
-    @property
-    def qubit_info(self):
-        """Dictionary with the qubit's IDs and positions."""
-        return self._root.qubit_info
-
-    @property
-    def declared_channels(self):
-        """Channels declared in this SequenceBuilder."""
-        return self._root.declared_channels
-
-    @property
-    def declared_variables(self):
-        """Variables declared in this SequenceBuilder."""
-        return dict(self._variables)
-
-    @property
-    def available_channels(self):
-        """Channels still available for declaration."""
-        return self._root.available_channels
-
-    def declare_variable(self, name, size=1, dtype=float):
-        """Declare a new variable within this SequenceBuilder.
-
-        Args:
-            name(str): The name for the variable. Must be unique within a
-                SequenceBuilder.
-
-        Keyword Args:
-            size(int=1): The number of entries stored in the variable.
-            dtype(default=float): The type of the data that will be assigned
-                to the variable. Must be `float`, `int` or `str`.
-
-        Returns:
-            Variable: The declared Variable instance.
-
-        Note:
-            To avoid confusion, it is recommended to store the returned
-            Variable instance in a Python variable with the same name.
-        """
-        if name in self._variables:
-            raise ValueError("Name for variable is already being used.")
-        var = Variable(name, dtype, size=size)
-        self._variables[name] = var
-        return var
-
-    def declare_channel(self, name, channel_id, initial_target=None):
-        """Declares a new channel to the Sequence.
-
-        Args:
-            name (str): Unique name for the channel in the sequence.
-            channel_id (str): How the channel is identified in the device.
-                Consult ``SequenceBuilder.available_channels`` to see which
-                channel ID's are still available and the associated channel's
-                description.
-
-        Keyword Args:
-            initial_target (set, default=None): For 'Local' adressing channels
-                only. Declares the initial target of the channel. If left as
-                None, the initial target will have to be set manually as the
-                first addition to this channel.
-        """
-
-        try:
-            self._root.declare_channel(name, channel_id,
-                                       initial_target=initial_target)
-        except ValueError as e:
-            if str(e) == "The given qubits have to belong to the device.":
-                raise TypeError("The initial target has to be a fixed qubit "
-                                "ID belonging to the device. Use 'target' for"
-                                " a variable initial target.")
-            else:
-                raise e
-
-    @_store
-    def add(self, pulse, channel, protocol='min-delay'):
-        """Adds a pulse to a channel.
-
-        Args:
-            pulse (pulser.Pulse): The pulse object to add to the channel.
-            channel (str): The channel's name provided when declared.
-
-        Keyword Args:
-            protocol (default='min-delay'): Stipulates how to deal with
-                eventual conflicts with other channels, specifically in terms
-                of having to channels act on the same target simultaneously.
-
-                - 'min-delay'
-                    Before adding the pulse, introduces the smallest
-                    possible delay that avoids all exisiting conflicts.
-                - 'no-delay'
-                    Adds the pulse to the channel, regardless of
-                    existing conflicts.
-                - 'wait-for-all'
-                    Before adding the pulse, adds a delay that
-                    idles the channel until the end of the other channels'
-                    latest pulse.
-        """
-
-        self._validate_channel(channel)
-        # TODO: Restrict variables in pulse according to channel specs
-
-        valid_protocols = ['min-delay', 'no-delay', 'wait-for-all']
-        if protocol not in valid_protocols:
-            raise ValueError(f"Invalid protocol '{protocol}', only accepts "
-                             "protocols: " + ", ".join(valid_protocols))
-
-    @_store
-    def target(self, qubits, channel):
-        """Changes the target qubit of a 'Local' channel.
-
-        Args:
-            qubits (hashable, iterable): The new target for this channel. Must
-                correspond to a qubit ID in device or an iterable of qubit IDs,
-                when multi-qubit adressing is possible.
-            channel (str): The channel's name provided when declared. Must be
-                a channel with 'Local' addressing.
-         """
-
-        self._validate_channel(channel)
-
-        try:
-            qs = set(qubits) if not isinstance(qubits, str) else {qubits}
-        except TypeError:
-            qs = {qubits}
-
-        if self._root._channels[channel].addressing != 'Local':
-            raise ValueError("Can only choose target of 'Local' channels.")
-        elif len(qs) > self._root._channels[channel].max_targets:
-            raise ValueError(
-                "This channel can target at most "
-                f"{self._root._channels[channel].max_targets} qubits at a time"
-            )
-
-    @_store
-    def delay(self, duration, channel):
-        """Idles a given channel for a specific duration.
-
-        Args:
-            duration (int): Time to delay (in multiples of 4 ns).
-            channel (str): The channel's name provided when declared.
-        """
-        self._validate_channel(channel)
-
-    @_store
-    def measure(self, basis='ground-rydberg'):
-        """Measures in a valid basis.
-
-        Args:
-            basis(str): Valid basis for measurement (consult the
-                'supported_bases' attribute of the selected device for
-                the available options).
-        """
-        available = self._root._device.supported_bases
-        if basis not in available:
-            raise ValueError(f"The basis '{basis}' is not supported by the "
-                             "selected device. The available options are: "
-                             + ", ".join(list(available)))
-
-        self._measurement = basis
-
-    @_store
-    def phase_shift(self, phi, *targets, basis='digital'):
-        r"""Shifts the phase of a qubit's reference by 'phi', for a given basis.
-
-        This is equivalent to an :math:`R_z(\phi)` gate (i.e. a rotation of the
-        target qubit's state by an angle :math:`\phi` around the z-axis of the
-        Bloch sphere).
-
-        Args:
-            phi (float): The intended phase shift (in rads).
-            targets: The ids of the qubits on which to apply the phase
-                shift.
-
-        Keyword Args:
-            basis(str): The basis (i.e. electronic transition) to associate
-                the phase shift to. Must correspond to the basis of a declared
-                channel.
-        """
-
-        if basis not in self._root._phase_ref:
-            raise ValueError("No declared channel targets the given 'basis'.")
-
-    @_store
-    def align(self, *channels):
-        """Aligns multiple channels in time.
-
-        Introduces delays that align the provided channels with the one that
-        finished the latest, such that the next action added to any of them
-        will start right after the latest channel has finished.
-
-        Args:
-            channels (str): The names of the channels to align, as given upon
-                declaration.
-        """
-
-        ch_set = set(channels)
-        # channels have to be a subset of the declared channels
-        if not ch_set <= set(self._root._channels):
-            raise ValueError("All channel names must correspond to declared"
-                             " channels.")
-        if len(channels) != len(ch_set):
-            raise ValueError("The same channel was provided more than once.")
-
-        if len(channels) < 2:
-            raise ValueError("Needs at least two channels for alignment.")
-
-    def build(self, **vars):
-        """Builds the sequence from the programmed instructions.
-
-        Keyword Args:
-            vars: The values for all the variables declared in this
-                SequenceBuilder instance, indexed by the name given upon
-                declaration. Check `SequenceBuilder.declared_variables` to see
-                all the variables.
-
-        Returns:
-            Sequence: The Sequence built with the given variable values.
-
-        Example:
-            # Check which variables are declared
-            >>> print(seq_builder.declared_variables)
-            {'x': Variable(name='x', dtype=<class 'float'>, size=1),
-             'y': Variable(name='y', dtype=<class 'int'>, size=3)}
-            # Build a sequence with specific values for both variables
-            >>> seq = seq_builder.build(x=0.5, y=[1, 2, 3])
-        """
-        all_keys, given_keys = self._variables.keys(), vars.keys()
-        if given_keys != all_keys:
-            invalid_vars = given_keys - all_keys
-            if invalid_vars:
-                warnings.warn("No declared variables named: "
-                              + ", ".join(invalid_vars))
-                for k in invalid_vars:
-                    vars.pop(k, None)
-            missing_vars = all_keys - given_keys
-            if missing_vars:
-                raise TypeError("Did not receive values for variables: "
-                                + ", ".join(missing_vars))
-
-        for name, value in vars.items():
-            self._variables[name]._assign(value)
-
-        seq = copy.deepcopy(self._root)
-        for call in self._calls:
-            args_ = [arg.build() if isinstance(arg, Parametrized) else arg
-                     for arg in call.args]
-            kwargs_ = {key: val.build() if isinstance(val, Parametrized)
-                       else val for key, val in call.kwargs.items()}
-            getattr(seq, call.name)(*args_, **kwargs_)
-
-        return seq
-
-    def __getattr__(self, name):
-        if hasattr(self._root, name):
-            raise AttributeError(f"'{name}' attribute of 'Sequence' is not "
-                                 "available in 'SequenceBuilder'.")
-        else:
-            raise AttributeError(
-                f"'SequenceBuilder' object has no attribute '{name}'."
-                )
-
-    def __str__(self):
-        prelude = "Prelude\n-------\n" + str(self._root)
-        lines = ["Stored calls\n------------"]
-        for i, c in enumerate(self._calls, 1):
-            args = [str(a) for a in c.args]
-            kwargs = [f"{key}={str(value)}" for key, value in c.kwargs.items()]
-            lines.append(f"{i}. {c.name}({', '.join(args+kwargs)})")
-        return prelude + "\n\n".join(lines)
-
-    def _validate_channel(self, channel):
-        if channel not in self._root._channels:
-            raise ValueError("Use the name of a declared channel.")
diff --git a/pulser/sequence.py b/pulser/sequence.py
index 5de7c67..03a98af 100644
--- a/pulser/sequence.py
+++ b/pulser/sequence.py
@@ -15,6 +15,9 @@
 from collections import namedtuple
 from collections.abc import Iterable
 import copy
+from functools import wraps
+from itertools import chain
+import json
 import warnings
 
 import numpy as np
@@ -22,11 +25,61 @@ import numpy as np
 import pulser
 from pulser.pulse import Pulse
 from pulser.devices import MockDevice
+from pulser._json_coders import PulserEncoder, PulserDecoder
+from pulser.parametrized import Parametrized, Variable
 from pulser._seq_drawer import draw_sequence
-from pulser.utils import validate_duration
+from pulser.utils import validate_duration, obj_to_dict
 
 # Auxiliary class to store the information in the schedule
 _TimeSlot = namedtuple('_TimeSlot', ['type', 'ti', 'tf', 'targets'])
+# Encodes a sequence building calls
+_Call = namedtuple("_Call", ['name', 'args', 'kwargs'])
+
+
+def _screen(func):
+    """Blocks the call to a function if the Sequence is parametrized."""
+    @wraps(func)
+    def wrapper(self, *args, **kwargs):
+        if self.is_parametrized():
+            raise RuntimeError(f"Sequence.{func.__name__} can't be called in"
+                               + " parametrized sequences.")
+        return func(self, *args, **kwargs)
+    return wrapper
+
+
+def _store(func):
+    """Stores any Sequence building call for deferred execution."""
+    @wraps(func)
+    def wrapper(self, *args, **kwargs):
+        def verify_variable(x):
+            if isinstance(x, Parametrized):
+                # If not already, the sequence becomes parametrized
+                self._building = False
+                for name, var in x.variables.items():
+                    if name not in self._variables:
+                        raise ValueError(f"Unknown variable '{name}'.")
+                    elif self._variables[name] is not var:
+                        raise ValueError(
+                            f"{x} has variables that don't come from this "
+                            "Sequence. Use only what's returned by this"
+                            "Sequence's 'declare_variable' method as your"
+                            "variables."
+                            )
+            elif isinstance(x, Iterable) and not isinstance(x, str):
+                # Recursively look for parametrized objs inside the arguments
+                for y in x:
+                    verify_variable(y)
+
+        if self._is_measured and self.is_parametrized():
+            raise SystemError("The sequence has been measured, no further "
+                              "changes are allowed.")
+        # Check if all Parametrized inputs stem from declared variables
+        for x in chain(args, kwargs.values()):
+            verify_variable(x)
+        storage = self._calls if self._building else self._to_build_calls
+        func(self, *args, **kwargs)
+        storage.append(_Call(func.__name__, args, kwargs))
+    return wrapper
 
 
 class Sequence:
@@ -39,10 +92,32 @@ class Sequence:
         - The device's channels that are used
         - The schedule of operations on each channel
 
+    A Sequence also supports variable parameters, which have to be obtained
+    through `Sequence.declare_variable()`. From the moment a variable is
+    declared, a `Sequence` becomes "parametrized" and stops being built on the
+    fly, instead storing the sequence building calls for later execution. This
+    forgoes some specific functionalities of a "regular" `Sequence`, like the
+    ability to validate a `Pulse` or to `draw` the sequence as it is being
+    built. Instead, all validation happens upon building (through
+    `Sequence.build()`), where values for all declared variables have to be
+    specified and a "regular" `Sequence` is created and returned. By changing
+    the values given to the variables, multiple sequences can be generated from
+    a single "parametrized" `Sequence`.
+
     Args:
         register(Register): The atom register on which to apply the pulses.
         device(PasqalDevice): A valid device in which to execute the Sequence
             (import it from ``pulser.devices``).
+
+    Notes:
+        The declared variables can be used to create parametrized versions of
+        Waveform and Pulse objects, which in turn can be added to the Sequence.
+        Additionally, simple arithmetic operations involving variables are also
+        supported and will return parametrized objects that are dependent on
+        the involved variables.
+
+        The register and device do not support variable parameters. As such,
+        they are the same for all Sequences built from a parametrized Sequence.
     """
     def __init__(self, register, device):
         """Initializes a new pulse sequence."""
@@ -59,6 +134,7 @@ class Sequence:
 
         self._register = register
         self._device = device
+        self._calls = [_Call("__init__", (register, device), {})]
         self._channels = {}
         self._schedule = {}
         self._phase_ref = {}  # The phase reference of each channel
@@ -68,6 +144,9 @@ class Sequence:
         self._last_used = {}    # Last time each qubit was used, by basis
         self._last_target = {}  # Last time a target happened, by channel
 
+        # Initializes all parametrized Sequence related attributes
+        self._reset_parametrized()
+
     @property
     def qubit_info(self):
         """Dictionary with the qubit's IDs and positions."""
@@ -78,6 +157,11 @@ class Sequence:
         """Channels declared in this Sequence."""
         return dict(self._channels)
 
+    @property
+    def declared_variables(self):
+        """Variables declared in this Sequence."""
+        return dict(self._variables)
+
     @property
     def available_channels(self):
         """Channels still available for declaration."""
@@ -85,6 +169,17 @@ class Sequence:
                 if id not in self._taken_channels
                 or self._device == MockDevice}
 
+    def is_parametrized(self):
+        """States whether the sequence is parametrized.
+
+        A parametrized sequence is one that depends on the values assigned to
+        variables declared within it. Sequence-building calls are not executed
+        right away, but rather stored for deferred execution when all variables
+        are given a value (when`Sequence.build()` is called).
+        """
+        return not self._building
+
+    @_screen
     def current_phase_ref(self, qubit, basis='digital'):
         """Current phase reference of a specific qubit for a given basis.
 
@@ -149,8 +244,54 @@ class Sequence:
         if ch.addressing == 'Global':
             self._add_to_schedule(name, _TimeSlot('target', -1, 0, self._qids))
         elif initial_target is not None:
-            self.target(initial_target, name)
+            try:
+                cond = any(isinstance(t, Parametrized) for t in initial_target)
+            except TypeError:
+                cond = isinstance(initial_target, Parametrized)
+            if cond:
+                self._building = False
+
+            if self.is_parametrized():
+                # Do not store "initial_target" in a _call when parametrized
+                # It is stored as a _to_build_call when target is called
+                self.target(initial_target, name)
+                initial_target = None
+            else:
+                # "_target" call is not saved
+                self._target(initial_target, name)
+
+        # Manually store the channel declaration as a regular call
+        self._calls.append(_Call(
+                            "declare_channel",
+                            (name, channel_id),
+                            {"initial_target": initial_target}))
+
+    def declare_variable(self, name, size=1, dtype=float):
+        """Declare a new variable within this Sequence.
+
+        Args:
+            name(str): The name for the variable. Must be unique within a
+                Sequence.
+
+        Keyword Args:
+            size(int=1): The number of entries stored in the variable.
+            dtype(default=float): The type of the data that will be assigned
+                to the variable. Must be `float`, `int` or `str`.
+
+        Returns:
+            Variable: The declared Variable instance.
 
+        Notes:
+            To avoid confusion, it is recommended to store the returned
+            Variable instance in a Python variable with the same name.
+        """
+        if name in self._variables:
+            raise ValueError("Name for variable is already being used.")
+        var = Variable(name, dtype, size=size)
+        self._variables[name] = var
+        return var
+
+    @_store
     def add(self, pulse, channel, protocol='min-delay'):
         """Adds a pulse to a channel.
 
@@ -174,15 +315,20 @@ class Sequence:
                     idles the channel until the end of the other channels'
                     latest pulse.
         """
-
-        last = self._last(channel)
-        self._validate_pulse(pulse, channel)
+        self._validate_channel(channel)
 
         valid_protocols = ['min-delay', 'no-delay', 'wait-for-all']
         if protocol not in valid_protocols:
             raise ValueError(f"Invalid protocol '{protocol}', only accepts "
                              "protocols: " + ", ".join(valid_protocols))
 
+        if self.is_parametrized():
+            if not isinstance(pulse, Parametrized):
+                self._validate_pulse(pulse, channel)
+            return
+
+        self._validate_pulse(pulse, channel)
+        last = self._last(channel)
         t0 = last.tf    # Preliminary ti
         basis = self._channels[channel].basis
         phase_barriers = [self._phase_ref[basis][q].last_time
@@ -203,7 +349,7 @@ class Sequence:
         ti = current_max_t
         tf = ti + pulse.duration
         if ti > t0:
-            self.delay(ti-t0, channel)
+            self._delay(ti-t0, channel)
 
         prs = {self._phase_ref[basis][q].last_phase for q in last.targets}
         if len(prs) != 1:
@@ -225,9 +371,10 @@ class Sequence:
                 self._last_used[basis][q] = tf
 
         if pulse.post_phase_shift:
-            self.phase_shift(pulse.post_phase_shift, *last.targets,
-                             basis=basis)
+            self._phase_shift(pulse.post_phase_shift, *last.targets,
+                              basis=basis)
 
+    @_store
     def target(self, qubits, channel):
         """Changes the target qubit of a 'Local' channel.
 
@@ -238,55 +385,9 @@ class Sequence:
             channel (str): The channel's name provided when declared. Must be
                 a channel with 'Local' addressing.
          """
+        self._target(qubits, channel)
 
-        if channel not in self._channels:
-            raise ValueError("Use the name of a declared channel.")
-
-        if isinstance(qubits, Iterable) and not isinstance(qubits, str):
-            qs = set(qubits)
-        else:
-            qs = {qubits}
-
-        if not qs.issubset(self._qids):
-            raise ValueError("The given qubits have to belong to the device.")
-
-        if self._channels[channel].addressing != 'Local':
-            raise ValueError("Can only choose target of 'Local' channels.")
-        elif len(qs) > self._channels[channel].max_targets:
-            raise ValueError(
-                "This channel can target at most "
-                f"{self._channels[channel].max_targets} qubits at a time"
-            )
-
-        basis = self._channels[channel].basis
-        phase_refs = {self._phase_ref[basis][q].last_phase for q in qs}
-        if len(phase_refs) != 1:
-            raise ValueError("Cannot target multiple qubits with different "
-                             "phase references for the same basis.")
-
-        try:
-            last = self._last(channel)
-            if last.targets == qs:
-                warnings.warn("The provided qubits are already the target. "
-                              "Skipping this target instruction.")
-                return
-            ti = last.tf
-            retarget = self._channels[channel].retarget_time
-            elapsed = ti - self._last_target[channel]
-            delta = np.clip(retarget - elapsed, 0, retarget)
-            if delta != 0:
-                with warnings.catch_warnings():
-                    warnings.simplefilter("ignore")
-                    delta = validate_duration(np.clip(delta, 16, np.inf))
-            tf = ti + delta
-
-        except ValueError:
-            ti = -1
-            tf = 0
-
-        self._last_target[channel] = tf
-        self._add_to_schedule(channel, _TimeSlot('target', ti, tf, qs))
-
+    @_store
     def delay(self, duration, channel):
         """Idles a given channel for a specific duration.
 
@@ -294,12 +395,9 @@ class Sequence:
             duration (int): Time to delay (in multiples of 4 ns).
             channel (str): The channel's name provided when declared.
         """
-        last = self._last(channel)
-        ti = last.tf
-        tf = ti + validate_duration(duration)
-        self._add_to_schedule(channel,
-                              _TimeSlot('delay', ti, tf, last.targets))
+        self._delay(duration, channel)
 
+    @_store
     def measure(self, basis='ground-rydberg'):
         """Measures in a valid basis.
 
@@ -314,11 +412,15 @@ class Sequence:
                              "selected device. The available options are: "
                              + ", ".join(list(available)))
 
-        if hasattr(self, '_measurement'):
+        if hasattr(self, "_measurement"):
             raise SystemError("The sequence has already been measured.")
 
-        self._measurement = basis
+        if self.is_parametrized():
+            self._is_measured = True
+        else:
+            self._measurement = basis
 
+    @_store
     def phase_shift(self, phi, *targets, basis='digital'):
         r"""Shifts the phase of a qubit's reference by 'phi', for a given basis.
 
@@ -336,22 +438,9 @@ class Sequence:
                 the phase shift to. Must correspond to the basis of a declared
                 channel.
         """
-        if phi % (2*np.pi) == 0:
-            warnings.warn("A phase shift of 0 is meaningless, "
-                          "it will be ommited.")
-            return
-        if not set(targets) <= self._qids:
-            raise ValueError("All given targets have to be qubit ids declared"
-                             " in this sequence's device.")
-
-        if basis not in self._phase_ref:
-            raise ValueError("No declared channel targets the given 'basis'.")
-
-        for q in targets:
-            t = self._last_used[basis][q]
-            new_phase = self._phase_ref[basis][q].last_phase + phi
-            self._phase_ref[basis][q][t] = new_phase
+        self._phase_shift(phi, *targets, basis=basis)
 
+    @_store
     def align(self, *channels):
         """Aligns multiple channels in time.
 
@@ -375,18 +464,216 @@ class Sequence:
         if len(channels) < 2:
             raise ValueError("Needs at least two channels for alignment.")
 
+        if self.is_parametrized():
+            return
+
         last_ts = {id: self._last(id).tf for id in channels}
         tf = max(last_ts.values())
 
         for id in channels:
             delta = tf - last_ts[id]
             if delta > 0:
-                self.delay(delta, id)
+                self._delay(delta, id)
+
+    def build(self, **vars):
+        """Builds a sequence from the programmed instructions.
+
+        Keyword Args:
+            vars: The 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.
 
+        Returns:
+            Sequence: The Sequence built with the given variable values.
+
+        Example:
+            # Check which variables are declared
+            >>> print(seq.declared_variables)
+            {'x': Variable(name='x', dtype=<class 'float'>, size=1),
+             'y': Variable(name='y', dtype=<class 'int'>, size=3)}
+            # Build a sequence with specific values for both variables
+            >>> seq1 = seq.build(x=0.5, y=[1, 2, 3])
+        """
+        if not self.is_parametrized():
+            warnings.warn("Building a non-parametrized sequence simply returns"
+                          " a copy of itself.")
+            return copy.copy(self)
+        all_keys, given_keys = self._variables.keys(), vars.keys()
+        if given_keys != all_keys:
+            invalid_vars = given_keys - all_keys
+            if invalid_vars:
+                warnings.warn("No declared variables named: "
+                              + ", ".join(invalid_vars))
+                for k in invalid_vars:
+                    vars.pop(k, None)
+            missing_vars = all_keys - given_keys
+            if missing_vars:
+                raise TypeError("Did not receive values for variables: "
+                                + ", ".join(missing_vars))
+
+        for name, value in vars.items():
+            self._variables[name]._assign(value)
+
+        # Shallow copy with stored parametrized objects
+        seq = copy.copy(self)
+        # Eliminates the source of recursiveness errors
+        seq._reset_parametrized()
+        # Deepcopy the base sequence (what remains)
+        seq = copy.deepcopy(seq)
+
+        for call in self._to_build_calls:
+            args_ = [arg.build() if isinstance(arg, Parametrized) else arg
+                     for arg in call.args]
+            kwargs_ = {key: val.build() if isinstance(val, Parametrized)
+                       else val for key, val in call.kwargs.items()}
+            getattr(seq, call.name)(*args_, **kwargs_)
+
+        return seq
+
+    def serialize(self, **kwargs):
+        """Serializes the Sequence into a JSON formatted string.
+
+        Other Parameters:
+            kwargs: Valid keyword-arguments for `json.dumps()`, except for
+                `cls`.
+
+        Returns:
+            str: The sequence encoded in a JSON formatted string.
+
+        See also:
+            json.dumps: Built-in function for serialization to a JSON formatted
+                string.
+        """
+        return json.dumps(self, cls=PulserEncoder, **kwargs)
+
+    @staticmethod
+    def deserialize(obj, **kwargs):
+        """Deserializes a JSON formatted string.
+
+        Args:
+            obj(str): The JSON formatted string to deserialize, coming from the
+                serialization of a `Sequence` through `Sequence.serialize()`.
+
+        Other Parameters:
+            kwargs: Valid keyword-arguments for `json.loads()`, except for
+                `cls` and `object_hook`.
+
+        Returns:
+            Sequence: The deserialized Sequence object.
+
+        See also:
+            json.loads: Built-in function for deserialization from a JSON
+                formatted string.
+        """
+        if "Sequence" not in obj:
+            warnings.warn("The given JSON formatted string does not encode a "
+                          "Sequence.")
+
+        return json.loads(obj, cls=PulserDecoder, **kwargs)
+
+    @_screen
     def draw(self):
         """Draws the sequence in its current sequence."""
         draw_sequence(self)
 
+    def _target(self, qubits, channel):
+        self._validate_channel(channel)
+
+        try:
+            qs = set(qubits) if not isinstance(qubits, str) else {qubits}
+        except TypeError:
+            qs = {qubits}
+
+        if self._channels[channel].addressing != 'Local':
+            raise ValueError("Can only choose target of 'Local' channels.")
+        elif len(qs) > self._channels[channel].max_targets:
+            raise ValueError(
+                "This channel can target at most "
+                f"{self._channels[channel].max_targets} qubits at a time"
+            )
+
+        if self.is_parametrized():
+            for q in qs:
+                if q not in self._qids and not isinstance(q, Parametrized):
+                    raise ValueError("All non-variable qubits must belong to "
+                                     "the register.")
+            return
+
+        elif not qs.issubset(self._qids):
+            raise ValueError("All given qubits must belong to the register.")
+
+        basis = self._channels[channel].basis
+        phase_refs = {self._phase_ref[basis][q].last_phase for q in qs}
+        if len(phase_refs) != 1:
+            raise ValueError("Cannot target multiple qubits with different "
+                             "phase references for the same basis.")
+
+        try:
+            last = self._last(channel)
+            if last.targets == qs:
+                warnings.warn("The provided qubits are already the target. "
+                              "Skipping this target instruction.")
+                return
+            ti = last.tf
+            retarget = self._channels[channel].retarget_time
+            elapsed = ti - self._last_target[channel]
+            delta = np.clip(retarget - elapsed, 0, retarget)
+            if delta != 0:
+                with warnings.catch_warnings():
+                    warnings.simplefilter("ignore")
+                    delta = validate_duration(np.clip(delta, 16, np.inf))
+            tf = ti + delta
+
+        except ValueError:
+            ti = -1
+            tf = 0
+
+        self._last_target[channel] = tf
+        self._add_to_schedule(channel, _TimeSlot('target', ti, tf, qs))
+
+    def _delay(self, duration, channel):
+        self._validate_channel(channel)
+        if self.is_parametrized():
+            return
+
+        last = self._last(channel)
+        ti = last.tf
+        tf = ti + validate_duration(duration)
+        self._add_to_schedule(channel,
+                              _TimeSlot('delay', ti, tf, last.targets))
+
+    def _phase_shift(self, phi, *targets, basis='digital'):
+        if basis not in self._phase_ref:
+            raise ValueError("No declared channel targets the given 'basis'.")
+        if self.is_parametrized():
+            for t in targets:
+                if t not in self._qids and not isinstance(t, Parametrized):
+                    raise ValueError("All non-variable targets must belong to "
+                                     "the register.")
+            return
+
+        elif not set(targets) <= self._qids:
+            raise ValueError("All given targets have to be qubit ids declared"
+                             " in this sequence's register.")
+
+        if phi % (2*np.pi) == 0:
+            warnings.warn("A phase shift of 0 is meaningless, "
+                          "it will be ommited.")
+            return
+
+        for q in targets:
+            t = self._last_used[basis][q]
+            new_phase = self._phase_ref[basis][q].last_phase + phi
+            self._phase_ref[basis][q][t] = new_phase
+
+    def _to_dict(self):
+        d = obj_to_dict(self, *self._calls[0].args, **self._calls[0].kwargs)
+        d["__version__"] = pulser.__version__
+        d["calls"] = self._calls[1:]
+        d["vars"] = self._variables
+        d["to_build_calls"] = self._to_build_calls
+        return d
+
     def __str__(self):
         full = ""
         pulse_line = "t: {}->{} | {} | Targets: {}\n"
@@ -415,12 +702,21 @@ class Sequence:
                     else:
                         full += target_line.format(ts.ti, ts.tf, tgt_txt,
                                                    phase)
-
             full += "\n"
 
         if hasattr(self, "_measurement"):
             full += f"Measured in basis: {self._measurement}"
 
+        if self.is_parametrized():
+            prelude = "Prelude\n-------\n" + full
+            lines = ["Stored calls\n------------"]
+            for i, c in enumerate(self._to_build_calls, 1):
+                args = [str(a) for a in c.args]
+                kwargs = [f"{key}={str(value)}"
+                          for key, value in c.kwargs.items()]
+                lines.append(f"{i}. {c.name}({', '.join(args+kwargs)})")
+            full = prelude + "\n\n".join(lines)
+
         return full
 
     def _add_to_schedule(self, channel, timeslot):
@@ -431,13 +727,15 @@ class Sequence:
 
     def _last(self, channel):
         """Shortcut to last element in the channel's schedule."""
-        if channel not in self._schedule:
-            raise ValueError("Use the name of a declared channel.")
         try:
             return self._schedule[channel][-1]
         except IndexError:
             raise ValueError("The chosen channel has no target.")
 
+    def _validate_channel(self, channel):
+        if channel not in self._channels:
+            raise ValueError("Use the name of a declared channel.")
+
     def _validate_pulse(self, pulse, channel):
         if not isinstance(pulse, Pulse):
             raise TypeError("pulse input must be of type Pulse, not of type "
@@ -452,6 +750,14 @@ class Sequence:
             raise ValueError("The pulse's detuning values go out of the range "
                              "allowed for the chosen channel.")
 
+    def _reset_parametrized(self):
+        """Resets all attributes related to parametrization."""
+        # Signals the sequence as actively "building" ie not parametrized
+        self._building = True
+        self._is_measured = False
+        self._variables = {}
+        self._to_build_calls = []
+
 
 class _PhaseTracker:
     """Tracks a phase reference over time."""
diff --git a/pulser/utils.py b/pulser/utils.py
index b668279..1f2385e 100644
--- a/pulser/utils.py
+++ b/pulser/utils.py
@@ -15,6 +15,36 @@
 import warnings
 
 
+def obj_to_dict(obj, *args, _build=True, _module=None, _name=None, **kwargs):
+    """Encodes an object in a dictionary for serialization.
+
+    Args:
+        obj: The object to encode in the dictionary.
+
+    Other parameters:
+        _build (bool): Whether the object is to be built on deserialization.
+        _module (str): Custom name for the module containing the object.
+        _name (str): Custom name of the object.
+        args: If the object is to be built, the arguments to give on creation.
+        kwargs: If the object is to be built, the keyword arguments to give on
+            creation.
+
+    Returns:
+        dict: The dictionary encoding the object.
+    """
+
+    d = {
+        "_build": _build,
+        "__module__": _module if _module else obj.__class__.__module__,
+        "__name__": _name if _name else obj.__class__.__name__,
+    }
+    if _build:
+        d["__args__"] = args
+        d["__kwargs__"] = kwargs
+
+    return d
+
+
 def validate_duration(duration, min_duration=16, max_duration=67108864):
     """Validates a time interval.
 
diff --git a/pulser/waveforms.py b/pulser/waveforms.py
index 0a69953..227f645 100644
--- a/pulser/waveforms.py
+++ b/pulser/waveforms.py
@@ -24,7 +24,7 @@ import matplotlib.pyplot as plt
 import numpy as np
 
 from pulser.parametrized import Parametrized, ParamObj
-from pulser.utils import validate_duration
+from pulser.utils import validate_duration, obj_to_dict
 
 
 class Waveform(ABC):
@@ -85,6 +85,10 @@ class Waveform(ABC):
 
         plt.show()
 
+    @abstractmethod
+    def _to_dict():
+        pass
+
     @abstractmethod
     def __str__(self):
         pass
@@ -205,6 +209,9 @@ class CompositeWaveform(Waveform):
             raise TypeError("{!r} is not a valid waveform. Please provide a "
                             "valid Waveform.".format(waveform))
 
+    def _to_dict(self):
+        return obj_to_dict(self, *self._waveforms)
+
     def __str__(self):
         contents = ["{!r}"] * len(self._waveforms)
         contents = ", ".join(contents)
@@ -254,6 +261,9 @@ class CustomWaveform(Waveform):
         """
         return self._samples
 
+    def _to_dict(self):
+        return obj_to_dict(self, self._samples)
+
     def __str__(self):
         return 'Custom'
 
@@ -301,6 +311,9 @@ class ConstantWaveform(Waveform):
         """The last value in the waveform."""
         return self._value
 
+    def _to_dict(self):
+        return obj_to_dict(self, self._duration, self._value)
+
     def __str__(self):
         return f"{self._value:.3g} rad/µs"
 
@@ -356,6 +369,9 @@ class RampWaveform(Waveform):
         """The last value in the waveform."""
         return self._stop
 
+    def _to_dict(self):
+        return obj_to_dict(self, self._duration, self._start, self._stop)
+
     def __str__(self):
         return f"Ramp({self._start:.3g}->{self._stop:.3g} rad/µs)"
 
@@ -436,6 +452,9 @@ class BlackmanWaveform(Waveform):
         """The last value in the waveform."""
         return 0
 
+    def _to_dict(self):
+        return obj_to_dict(self, self._duration, self._area)
+
     def __str__(self):
         return f"Blackman(Area: {self._area:.3g})"
 
diff --git a/tutorials/applications/QAOA and Parametrized Sequences.ipynb b/tutorials/applications/QAOA and Parametrized Sequences.ipynb
new file mode 100644
index 0000000..0d715c6
--- /dev/null
+++ b/tutorials/applications/QAOA and Parametrized Sequences.ipynb
@@ -0,0 +1,394 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# QAOA for the UD-MIS problem: Using a Parametrized Sequence"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np\n",
+    "import igraph\n",
+    "\n",
+    "import matplotlib.pyplot as plt\n",
+    "from itertools import combinations\n",
+    "from collections import Counter\n",
+    "\n",
+    "from pulser import Pulse, Sequence, Register, Simulation\n",
+    "from pulser.devices import Chadoq2\n",
+    "from pulser.waveforms import CustomWaveform\n",
+    "\n",
+    "from scipy.optimize import minimize"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 1. Introduction\n",
+    "\n",
+    "**Parametrized** Sequences are useful when we want to construct several `Sequence` instances while changing some of their defining parameters (for example the amplitude, the detuning or the duration of the pulses). \n",
+    "\n",
+    "This tutorial is based on the \"Using QAOA to solve a MIS problem\" tutorial. The construction process is not too different from the usual `Sequence` design, but it applies the parametrization of the sequences with a set of time variables, and then proceeds to build the actual sequences with each desired value."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. Register and Graph"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We use a parametrized Sequence for the QAOA solution of the MIS problem of the tutorials. We restrict the number of atoms to 5, just to show a proof-of-concept.\n",
+    "\n",
+    "A link in the graph corresponds to two atoms that are within the Rydberg Blockade Radius of each other. The radius is obtained using `Chadoq2.rydberg_blockade_radius()`. In this notebook, $\\Omega$ is fixed to a frequency of 1 rad/µs."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "Rb = Chadoq2.rydberg_blockade_radius(1.*2*np.pi)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def make_graph(coords):\n",
+    "    N = len(coords)\n",
+    "    g = igraph.Graph()\n",
+    "    edges = [[m,n] for m,n in combinations(range(N), r=2) \n",
+    "              if np.linalg.norm(coords[m] - coords[n]) < Rb]    \n",
+    "    g.add_vertices(N)\n",
+    "    g.add_edges(edges)\n",
+    "    return g"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Here, we create an atomic register with 5 atoms and the corresponding graph."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 252x234 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "coords = np.array([(0., 0.), (-4, -8), (4, -8), (7, 5), (-7, 5)])\n",
+    "G = make_graph(coords)\n",
+    "reg = Register.from_coordinates(coords)\n",
+    "reg.draw(blockade_radius=Rb, draw_graph=True, draw_half_radius=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This graph has two maximal independent sets: $(1,3,4)$ and $(2,3,4)$, respectively `010111` and `00111` in binary. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 2. Build parametrized sequences"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "layers = 2\n",
+    "guess = {'t': np.random.uniform(8, 10, layers),\n",
+    "         's': np.random.uniform(1, 3, layers)}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'t': array([8.69005125, 9.81977019]), 's': array([1.37372178, 1.55053409])}"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "guess"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Notice next how we construct the sequences with parametrized variables. This is done using the. `.declare_variable()` method. We will need two time values for each layer of the QAOA sequence (the \"Mixer\" and \"Cost\" parts), so we store these variables in two lists. Next, we construct the sequence as usual, where the methods are stored until the actual building takes place."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Parametrized sequence\n",
+    "seq = Sequence(reg, Chadoq2)\n",
+    "seq.declare_channel('ch0','rydberg_global')\n",
+    "\n",
+    "t_list = seq.declare_variable('t_list', size=layers)\n",
+    "s_list = seq.declare_variable('s_list', size=layers)\n",
+    "\n",
+    "if layers == 1:\n",
+    "    t_list = [t_list]\n",
+    "    s_list = [s_list]\n",
+    "    \n",
+    "for t, s in zip(t_list, s_list): \n",
+    "    pulse_1 = Pulse.ConstantPulse(1000*t, 1., 0., 0) \n",
+    "    pulse_2 = Pulse.ConstantPulse(1000*s, 1., 1., 0)\n",
+    "\n",
+    "    seq.add(pulse_1, 'ch0')\n",
+    "    seq.add(pulse_2, 'ch0')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Once we have the parameters that we want to apply, we use the `.build()` method to assign these values into a `assigned_sequence` sequence. It is this sequence which is simulated every time the quantum loop is called:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def quantum_loop(parameters):\n",
+    "    t_params, s_params = np.reshape(parameters, (2, layers))\n",
+    "    assigned_sequence = seq.build(t_list=t_params, s_list=s_params)\n",
+    "    assigned_sequence.measure('ground-rydberg')\n",
+    "    \n",
+    "    simul = Simulation(assigned_sequence, sampling_rate=.01)\n",
+    "    results = simul.run()\n",
+    "    return results.sample_final_state()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "/Users/hsilver/Documents/Pasqal_Code/Pulser/pulser/utils.py:68: UserWarning: The given duration is below the machine's precision of 4 ns time steps. It was rounded down to the nearest multiple of 4 ns.\n",
+      "  warnings.warn(\"The given duration is below the machine's precision\"\n"
+     ]
+    }
+   ],
+   "source": [
+    "example_dict = quantum_loop(np.r_[guess['t'], guess['s']])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can then plot the distribution of the samples, to see the most frequent bitstrings sampled."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def plot_distribution(results):\n",
+    "    C = dict(Counter(results).most_common())\n",
+    "    indexes = ['01011', '00111']  # MIS indexes\n",
+    "    color_dict = {key:'coral' if key in indexes else 'royalblue' for key in C}\n",
+    "    \n",
+    "    plt.figure(figsize=(12,6))\n",
+    "    plt.xlabel(\"bitstrings\")\n",
+    "    plt.ylabel(\"counts\")\n",
+    "    plt.bar(C.keys(), C.values(), width=0.5, color = color_dict.values())\n",
+    "    plt.xticks(rotation='vertical')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 864x432 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "plot_distribution(example_dict);"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## 3. Optimization "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The rest of the optimization takes place just like in the related tutorial:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def get_cost_colouring(bitstring, G, penalty=10):\n",
+    "    z = np.array(list(bitstring), dtype=int)\n",
+    "    A = np.array(G.get_adjacency().data)\n",
+    "    # Add penalty and bias:\n",
+    "    cost = penalty*(z.T @ np.triu(A) @ z) - np.sum(z)\n",
+    "    return cost \n",
+    "\n",
+    "def get_cost(res,G):\n",
+    "    counter = dict(res)\n",
+    "    cost = sum(counter[key] * get_cost_colouring(key,G) for key in counter) \n",
+    "    return cost / sum(counter.values()) # Divide by total samples"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def func(param,*args):\n",
+    "    G = args[0]\n",
+    "    C = quantum_loop(param)\n",
+    "    cost = get_cost(C,G)\n",
+    "    return cost"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "res = minimize(func, \n",
+    "               args=G,\n",
+    "               x0=np.r_[guess['t'], guess['s']],\n",
+    "               method='Nelder-Mead',\n",
+    "               tol=1e-5,\n",
+    "               options = {'maxiter': 60}\n",
+    "              )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "We can now plot the sample that we woud obtain using the variational parameters `res.x`. Each time the Quantum Loop is called, a new instance of a Sequence is built and simulated:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 864x432 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "count_dict = quantum_loop(res.x)\n",
+    "plot_distribution(count_dict)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "pulser-dev",
+   "language": "python",
+   "name": "pulser-dev"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.5"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/applications/QAOA and Sequence Builder.ipynb b/tutorials/applications/QAOA and Sequence Builder.ipynb
deleted file mode 100644
index 1a3507e..0000000
--- a/tutorials/applications/QAOA and Sequence Builder.ipynb
+++ /dev/null
@@ -1,381 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# QAOA for the UD-MIS problem: Using a Sequence Builder"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 1,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import numpy as np\n",
-    "import igraph\n",
-    "\n",
-    "import matplotlib.pyplot as plt\n",
-    "from itertools import combinations\n",
-    "from collections import Counter\n",
-    "\n",
-    "from pulser import Pulse, Sequence, SequenceBuilder, Register, Simulation\n",
-    "from pulser.devices import Chadoq2\n",
-    "from pulser.waveforms import CustomWaveform\n",
-    "\n",
-    "from scipy.optimize import minimize"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## 1. Register and Graph"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We use the Sequence Builder for the QAOA solution of the MIS problem of the tutorials. We restrict the number of atoms to 5, just to show a proof-of-concept.\n",
-    "\n",
-    "A link in the graph corresponds to two atoms that are within the Rydberg Blockade Radius of each other. The radius is obtained using `Chadoq2.rydberg_blockade_radius()`. In this notebook, $\\Omega$ is fixed to a frequency of 1 rad/µs."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 8,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "Rb = Chadoq2.rydberg_blockade_radius(1.*2*np.pi)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 9,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def make_graph(coords):\n",
-    "    N = len(coords)\n",
-    "    g = igraph.Graph()\n",
-    "    edges = [[m,n] for m,n in combinations(range(N), r=2) \n",
-    "              if np.linalg.norm(coords[m] - coords[n]) < Rb]    \n",
-    "    g.add_vertices(N)\n",
-    "    g.add_edges(edges)\n",
-    "    return g"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Here, we create an atomic register with 5 atoms and the corresponding graph."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 26,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "image/png": "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\n",
-      "text/plain": [
-       "<Figure size 252x234 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "coords = np.array([(0., 0.), (-4, -8), (4, -8), (7, 5), (-7, 5)])\n",
-    "G = make_graph(coords) \n",
-    "reg = Register.from_coordinates(coords)\n",
-    "reg.draw(blockade_radius=Rb, draw_graph=True, draw_half_radius=True)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "This graph has two maximal independent sets: $(1,3,4)$ and $(2,3,4)$, respectively `010111` and `00111` in binary. "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## 2. Build model"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 27,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "layers = 2\n",
-    "guess = {'t': np.random.uniform(8, 10, layers),\n",
-    "         's': np.random.uniform(1, 3, layers)}"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 28,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "{'t': array([9.95344455, 9.11593174]), 's': array([1.36990143, 2.93709875])}"
-      ]
-     },
-     "execution_count": 28,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
-   "source": [
-    "guess"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 29,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Parametrized sequence\n",
-    "seq_builder = SequenceBuilder(reg, Chadoq2)\n",
-    "seq_builder.declare_channel('ch0','rydberg_global')\n",
-    "\n",
-    "t_list = seq_builder.declare_variable('t_list', size=layers)\n",
-    "s_list = seq_builder.declare_variable('s_list', size=layers)\n",
-    "\n",
-    "if layers == 1:\n",
-    "    t_list = [t_list]\n",
-    "    s_list = [s_list]\n",
-    "    \n",
-    "for t, s in zip(t_list, s_list):\n",
-    "    pulse_1 = Pulse.ConstantPulse(1000*t, 1., 0., 0) \n",
-    "    pulse_2 = Pulse.ConstantPulse(1000*s, 1., 1., 0)\n",
-    "\n",
-    "    seq_builder.add(pulse_1, 'ch0')\n",
-    "    seq_builder.add(pulse_2, 'ch0')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 30,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def quantum_loop(parameters):\n",
-    "    t_params, s_params = np.reshape(parameters, (2, layers))\n",
-    "    seq = seq_builder.build(t_list=t_params, s_list=s_params)\n",
-    "    seq.measure('ground-rydberg')\n",
-    "    \n",
-    "    simul = Simulation(seq, sampling_rate=.01)\n",
-    "    results = simul.run()\n",
-    "    return results.sample_final_state()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 32,
-   "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "/Users/seb/Documents/Projects/pasqal/Pulser/pulser/utils.py:38: UserWarning: The given duration is below the machine's precision of 4 ns time steps. It was rounded down to the nearest multiple of 4 ns.\n",
-      "  warnings.warn(\"The given duration is below the machine's precision\"\n"
-     ]
-    }
-   ],
-   "source": [
-    "example_dict = quantum_loop(np.r_[guess['t'], guess['s']])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We can then plot the distribution of the samples, to see the most frequent bitstrings sampled."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 33,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def plot_distribution(results):\n",
-    "    C = dict(Counter(results).most_common())\n",
-    "    indexes = ['01011', '00111']  # MIS indexes\n",
-    "    color_dict = {key:'coral' if key in indexes else 'royalblue' for key in C}\n",
-    "    \n",
-    "    plt.figure(figsize=(12,6))\n",
-    "    plt.xlabel(\"bitstrings\")\n",
-    "    plt.ylabel(\"counts\")\n",
-    "    plt.bar(C.keys(), C.values(), width=0.5, color = color_dict.values())\n",
-    "    plt.xticks(rotation='vertical')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 34,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtQAAAGJCAYAAACq1aREAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAAAeYklEQVR4nO3de7B1Z10f8O8PAla5SGJiCLkYB4KApUSMiG3pSCjXmRqgXIIXQqCNM4A1ozMSdUaoSgteA9rQRrkE5SKVIikilwkoY2kgCYYQEoEMkpBIQgQCWEQh/PrH3i8573nPOTlvnr3P3iv5fGbOvHuvtc/ON+usvd7vWe+znlXdHQAA4La506oDAADAlCnUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAw5ZdYARhx9+eB9//PGrjgEAwO3cJZdc8nfdfcRW6yZdqI8//vhcfPHFq44BAMDtXFVdvd06Qz4AAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAw4JBVB5iqk597zcLf8z3nHLfw9wQAYLmcoQYAgAEKNQAADFCoAQBggDHUt3PGegMALJcz1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABiwtEJdVcdW1Xur6oqq+mhV/fR8+WFV9e6q+sT8z0Pny6uqXl5VV1XVZVX10GVlAwCARVnmGeqvJ/nZ7n5QkocneV5VPSjJWUku6O4Tklwwf54kj09ywvzrjCSvWGI2AABYiKUV6u7+THd/aP74y0muTHJ0klOSnDd/2XlJnjh/fEqS1/bMhUnuVVVHLSsfAAAswp6Moa6q45N8X5IPJDmyuz8zX3V9kiPnj49O8ukN33btfBkAAKytpRfqqrp7kjcnObO7v7RxXXd3kj7I9zujqi6uqotvvPHGBSYFAICDt9RCXVV3yaxMv667/9d88Q37hnLM//zsfPl1SY7d8O3HzJftp7vP7e6TuvukI444YnnhAQBgF5Y5y0cleWWSK7v7tzasOj/JafPHpyV564blz5zP9vHwJF/cMDQEAADW0iFLfO9/leQnknykqi6dL/uFJC9J8qaqek6Sq5M8bb7u7UmekOSqJF9JcvoSswEAwEIsrVB3918mqW1WP2qL13eS5y0rDwAALIM7JQIAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAOWVqir6lVV9dmqunzDshdV1XVVden86wkb1v18VV1VVR+rqscuKxcAACzSMs9QvybJ47ZY/tvdfeL86+1JUlUPSnJqku+df885VXXnJWYDAICFWFqh7u73Jfn8Ll9+SpI3dvc/dvffJLkqycOWlQ0AABZlFWOon19Vl82HhBw6X3Z0kk9veM2182UHqKozquriqrr4xhtvXHZWAADY0V4X6lckuW+SE5N8JslvHuwbdPe53X1Sd590xBFHLDgeAAAcnD0t1N19Q3ff3N3fSPJ7uWVYx3VJjt3w0mPmywAAYK3taaGuqqM2PH1Skn0zgJyf5NSq+paq+u4kJyT54F5mAwCA2+KQZb1xVb0hyQ8nObyqrk3ywiQ/XFUnJukkn0ryk0nS3R+tqjcluSLJ15M8r7tvXlY2AABYlKUV6u5+xhaLX7nD61+c5MXLygMAAMvgTokAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwYFeFuqp+uqruWTOvrKoPVdVjlh0OAADW3W7PUD+7u7+U5DFJDk3yE0lesrRUAAAwEbst1DX/8wlJ/qC7P7phGQAA3GHttlBfUlXvyqxQv7Oq7pHkG8uLBQAA03DILl/3nCQnJvlkd3+lqr4jyelLSwUAABOx2zPU7+7uD3X3TUnS3Z9L8ttLSwUAABOx4xnqqvpnSb4tyeFVdWhuGTd9zyRHLzkbAACsvVsb8vGTSc5Mcp8kl+SWQv2lJL+7vFgAADANOxbq7n5ZkpdV1U919+/sUSYAAJiMXV2U2N2/U1X/MsnxG7+nu1+7pFwAADAJuyrUVfUHSe6b5NIkN88XdxKFGgCAO7TdTpt3UpIHdXcvMwwAAEzNbqfNuzzJvZcZBAAApmi3Z6gPT3JFVX0wyT/uW9jdP7KUVAAAMBG7LdQvWmYIAACYqt3O8vEXyw4CAABTtNtZPr6c2aweSXLXJHdJ8v+6+57LCgYAAFOw2zPU99j3uKoqySlJHr6sUAAAMBW7neXjm3rmT5I8dvFxAABgWnY75OPJG57eKbN5qb+6lEQAADAhu53l499tePz1JJ/KbNgHAADcoe12DPXpyw4CAABTtKsx1FV1TFW9pao+O/96c1Uds+xwAACw7nZ7UeKrk5yf5D7zr/89XwYAAHdouy3UR3T3q7v76/Ov1yQ5Yom5AABgEnZbqD9XVT9eVXeef/14ks8tMxgAAEzBbgv1s5M8Lcn1ST6T5ClJnrWkTAAAMBm7nTbvl5Oc1t1fSJKqOizJb2RWtAEA4A5rt2eo/8W+Mp0k3f35JN+3nEgAADAduy3Ud6qqQ/c9mZ+h3u3ZbQAAuN3abaH+zST/t6p+pap+Jcn7k/zaTt9QVa+az1l9+YZlh1XVu6vqE/M/D50vr6p6eVVdVVWXVdVDb+v/EAAA7KVdFerufm2SJye5Yf715O7+g1v5ttckedymZWcluaC7T0hywfx5kjw+yQnzrzOSvGI3uQAAYNV2PWyju69IcsVBvP59VXX8psWnJPnh+ePzkvx5khfMl7+2uzvJhVV1r6o6qrs/s9v/HgAArMJuh3wsypEbSvL1SY6cPz46yac3vO7a+bIDVNUZVXVxVV184403Li8pAADswl4X6m+an43u2/B953b3Sd190hFHuFkjAACrtdeF+oaqOipJ5n9+dr78uiTHbnjdMfNlAACw1va6UJ+f5LT549OSvHXD8mfOZ/t4eJIvGj8NAMAULG0u6ap6Q2YXIB5eVdcmeWGSlyR5U1U9J8nVmd3OPEnenuQJSa5K8pUkpy8rFwAALNLSCnV3P2ObVY/a4rWd5HnLygIAAMuysosSAQDg9kChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBgaXdKhINx8nOvWfh7vuec4xb+ngAAmzlDDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAw5ZdQCYkpOfe83C3/M95xy38PcEAPaOM9QAADBAoQYAgAEKNQAADDCGGm6PXvSkJbznWxb/ngBwO6BQAyvjIk8Abg8M+QAAgAHOUAPcCmfSAdiJM9QAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwwJ0SAW4nFn1HR3dzBNgdhRqAvfOiJy3hPd+y+PcEOAiGfAAAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYsJIbu1TVp5J8OcnNSb7e3SdV1WFJ/ijJ8Uk+leRp3f2FVeQDAIDdWuUZ6kd294ndfdL8+VlJLujuE5JcMH8OAABrbZ2GfJyS5Lz54/OSPHF1UQAAYHdWVag7ybuq6pKqOmO+7Mju/sz88fVJjlxNNAAA2L2VjKFO8q+7+7qq+s4k766qv964sru7qnqrb5wX8DOS5Ljjjlt+UgAA2MFKzlB393XzPz+b5C1JHpbkhqo6Kknmf352m+89t7tP6u6TjjjiiL2KDAAAW9rzQl1Vd6uqe+x7nOQxSS5Pcn6S0+YvOy3JW/c6GwAAHKxVDPk4Mslbqmrff//13f2OqrooyZuq6jlJrk7ytBVkAwCAg7Lnhbq7P5nkIVss/1ySR+11HgAAGLFO0+YBAMDkKNQAADBAoQYAgAEKNQAADFCoAQBggEINAAADFGoAABigUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGHLLqAACwbk5+7jULf8/3nHPcwt8TWA/OUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAY4MYuADBRbkAD68EZagAAGKBQAwDAAIUaAAAGGEMNACyVsd7c3jlDDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMEChBgCAAQo1AAAMUKgBAGCAQg0AAAMUagAAGKBQAwDAAIUaAAAGHLLqAAAA6+Dk516z8Pd8zznHLfw9WT/OUAMAwACFGgAABijUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAYoFADAMAAhRoAAAYo1AAAMGDtCnVVPa6qPlZVV1XVWavOAwAAOzlk1QE2qqo7J/lvSR6d5NokF1XV+d19xWqTAQCsh5Ofe83C3/M95xy38Pe8I1m3M9QPS3JVd3+yu/8pyRuTnLLiTAAAsK21OkOd5Ogkn97w/NokP7iiLAAA3EZ3pDPp1d2rzvBNVfWUJI/r7v8wf/4TSX6wu5+/4TVnJDlj/vR7knxsz4MenMOT/N2qQ+yCnIsl52LJuVhyLpaciyXnYsm5ON/V3UdstWLdzlBfl+TYDc+PmS/7pu4+N8m5exlqRFVd3N0nrTrHrZFzseRcLDkXS87FknOx5FwsOffGuo2hvijJCVX13VV11ySnJjl/xZkAAGBba3WGuru/XlXPT/LOJHdO8qru/uiKYwEAwLbWqlAnSXe/PcnbV51jgaYyPEXOxZJzseRcLDkXS87FknOx5NwDa3VRIgAATM26jaEGAIBJUagBAGCAQg0AAAMUagAAGLB2s3xMWVUdkuQ5SZ6U5D7zxdcleWuSV3b311aVbbeq6pe6+5dXnWOfqnpskidmdlv6ZL49u/sdKwu1hank3E5VndvdZ9z6K/fG1LfnVFTV6d396lXnuDXrtH86zi9eVT0gySnZ//N+fndfubpUu7du25PVMMvHAlXVG5LclOS8JNfOFx+T5LQkh3X301cUbdeq6pruPm7VOZKkqs5Ocv8kr83+2/OZST7R3T+9omj7mVDOw7ZbleTD3X3MXubZzlS2ZzL94r9mn/ep7J+O8wtUVS9I8owkb8z+2/PUJG/s7pesKtturdn2nMQvfFPJeTAU6gWqqo939/0Pdt1eq6ovbbcqybd291r8y8V226yqKsnHu/uEFcQ6wIRy3pzk6sx+zvv0/PnR3X3XlQTbZELb8+xMoPhX1WXbrUpy/+7+lr3Ms52p75+3tm6vTek4n+R7Nxeo+d2SP7pGn/epbM9J/MI3lZwHYy12gNuRz1fVU5O8ubu/kSRVdackT03yhZUm299NSX6gu2/YvKKqPr33cbb11ar6ge6+aNPyH0jy1VUE2sZUcn4yyaO6+5rNK/zcb5MnbFP8/yjJx5OsRaFOcmSSx+bAY1Alef/ex9nWVPZPx/nF+kZmZyiv3rT8qPm6dXFTprE9v3+L49K1SS6c//KyLqaSc9cU6sU6NclLk5xTVfsOrPdK8t75unXx2iTfleSAA0OS1+9xlp08K8krquoeueU32GOTfHG+bl08K9PIeXaSQ5McUFiS/NreRtnRszKN7TmV4v+2JHfv7ks3r6iqP9/zNNs7O9PYPx3nF+vMJBdU1SeS7CumxyW5X5LnryrUFqayPafyC99Ucu6aIR9LUlXfkSTd/blVZ5m6qrp3NoxR7e7rV5lnO1PJORXrvj2r6qFJXpFkq+L/vO6+ZFXZ2BuO84sxL1IPy/7XIlzU3TevLtU0VdXxmf3Cd3JmxbQy+4XvPUnO6u6/WVm4DaaS82Ao1Hukqh7d3e9edY59qurbkzwu+x/A3tndN60s1BamknM7VfWA7v7rVee4NfbP227di3/yzfHnmwvLB3sifwGs2/65nXXLOZXP0e1g/1zL4/xUfuGbSs5bo1DvkTW7CviZSV6Y5F2ZHbiS2cUAj07yn7v7tavKttFUcu5knX7uO1mnnFP6uU+hsFTVY5Kck+QT2X973i/Jc7v7XavKtlvrtH/uZJ1yTuVzZP9cvG2mIXzrupX+qeTcLYV6garq/O1WJTm5u++2l3m2U1UfS/KDm//Sr6pDk3xgja5Sn0rOl2+3Kslp3X3PvcyzHfvnYk2osFyZ5PHd/alNy787ydu7+4ErCbbJhPbPqeScyudoKvvnVI7zk5iGcCo5D4aLEhfrEUl+PMnfb1q+75+z1kVlNh3VZt/I/lNWrdpUcp6e5GeT/OMW656xx1l2Yv9crF/M7Er1mzYu3FdYMruIaR0cklv+wtrouiR32eMsO5nK/jmVnFP5HE1l/5zKcf452Xoawt9K8tEk61JUp5Jz1xTqxbowyVe6+y82r5ifLVgXL07yoap6V/a/qvrRSX5lZakONJWcFyW5vLsPmIKsql6093G2Zf9crKkUllcluaiq3phbtuexmZ0JeuXKUh1oKvvnVHJO5XM0lf1zKsf5qUxDOJWcu2bIxx3U/CzaY3Pg2M+1mq5mCjlrdoe3r3b3V1ad5fZiIj/305L8UmZDPg4oLN39mhVFO0BVPSjJj+TAWztfsbpULNsUPkfJNPbPqRznq+pxSX43szHpB0xD2GtyF9ep5DwYCvUSVNWR2f+q/63mrWSXprQ95wfddPfnV52F5ZtKYdlnCvvnlD7vUzCl7TmF/TNZ/5xTmYZwKjl3y5CPBaqq78tsXtpvz4aLlKrqpsyuVv7QqrLtVlV9pLsfvOocSVJVJyb575ltz2sz+2f0tdueVXVcZjeeeFRmd9OqqrpnbplP81OrS7c76/Rz38m65ezuL1TVe7N/YVmrMr1h/zw5szmy13L/dPxcrAkeP9d9/5zScb43fO17vnbDKOY3dLlw8/Kqunt3b75GYe0p1Iv16iQ/2d0f2Liwqh4+X/eQlaTapKqevN2qJPfeyyy34jWZwPZM8keZ3eXtx/b9Zl1Vd87sjk9vTPLw1UW7xVR+7hPKeWImUFgykf0zjp+L9ppMYHtmOvvnJHLuNA1hVU1iGsIkV2Q2/GNSDPlYoKr6RHefsM26q7r7fnudaStV9bUkr8vWF1Q9pbvvsceRtjSh7blTzm3X7bUJ/dynkvPSbF9Y/kd3r0VhmdD+OZXP+1T2z6lsz9vD/rlOOacyDeHPbLcqyS9292F7mWcRnKFerD+rqj/NbLqsjVcrPzPJOg2wvyzJb3T35ZtXVNW/XUGe7Uxle15SVeckOS/75zwtyV+tLNWBpvJzn0rOu20u00nS3RdW1VrMRTw3lf1zKp/3qeyfU9meU9k/p5JzKtMQ/pckv57k61usu9MeZ1kIZ6gXrKoenwPv/HN+d799dan2V1WPSHJ1d1+zxbqTuvviFcTa0kS2510zm1PzgJxJXtndW81buuem8nOfUM6XJ7lvti4sf9Pdz19Vto2msn8mk/m8T2L/TCazPSexf04o588neVpmw1A2T0P4pu7+r6vKtlFVvT/JT3X3JVus+3R3H7uCWEMUaoDbaAqFBbhjqaoHZuvj0jpNQ/g9ST7f3Tduse7IdZ6NZjsK9R6pqnO7+4xV50iSqjoks9+0n5TZxOrJ7AP31sx+0/7adt+7LtZ0ez4x+x/A1mp7TuXnPpWcUzGV/XMna/p5n+z+uabb84lZ4/1zKjlZHYV6gfbNTbnVqiQf7u5j9jLPdqrqDZlN+3NebhlrdUxmY8EO6+6nryjafmzPxZJzsarq25P8fGZngo7M7CK1z2b2F+xLetMtyVdlQtvT532BbM/FmkrOnVTVn3X341edI9nv+PnEJN+ZNT1+HgyFeoGq6ubMbqO58bbDPX9+dHffdSXBNqmqj3f3/Q923V6zPRdLzsWqqndmNgfted19/XzZvZM8K8nJ3f2YFcb7pgltT5/3BbI9F2tCOR+63aokb+vuo/Yyz3Z2OH6eluRR63L8PBhm+VisT2a2I2x1scqnt3j9qny+qp6a5M3zidVTszsWPTXJOt2UwvZcLDkX6/jufunGBfO/GF5SVaevKNNWprI9fd4Xy/ZcrKnkvCjJX2T/X6T2udfeRtnRdsfPl1bVs1eUacgkpyZZY2cnOXSbdb+2hzluzalJnpLk+qr6eFV9PMn1SZ48X7cuzo7tuUhyLtbVVfVzNbu1c5LZxTRV9YLccnX9OpjK9jw7Pu+LdHZsz0WaSs4rM5sf/5Gbv5L83arDbTCV4+euGfKxYFX1gGx9de2Vq0t1oG2uAn7rGua0PRdIzsWpqkOTnJVZzu+cL74hs2m0XtJrdAvyKWzPxOd90WzPxZpCzqp6SpKPdPfHtlj3xO7+k71PdaApHT93yxnqBaqqn8ts7sdK8sH5VyV5Q1WdtcpsG81/A3x9ZuPpPjD/StYvp+25QHIuVnd/obtf0N0P6O7D5l8P7O4XZHahzVqYyvb0eV8s23OxppKzu/94qzI9t92/WOy5qRw/D4Yz1As0/yeg7908fU7NJoT/aK/PrUnlXCA5F2sqOXdSVdd093GrzpFMZ3vKuVhyLtZUcu5knY5LO5lKzs1clLhY38hsXtKrNy0/ar5uXci5WHIu1iRyVtVl263KbBq9dTGJ7Rk5F03OxZpEzqkcl6aS82Ao1It1ZpILquoTuWVQ/XFJ7pdkLW5DPHdm5FykMyPnIp2ZaeQ8Msljc+AV/pXk/XsfZ1tnZhrb88zIuUhnRs5FOjPTyDmV49JUcu6aIR8LNp9G52HZ/6KFi7r75tWlOpCciyXnYk0hZ1W9Msmru/svt1j3+u7+0RXE2tIUtmci56LJuVhTyDmV49JUch4MhRoAAAaY5QMAAAYo1AAAMEChBlhDVXV8VV2+xfLfr6oHzR//wi7e58yq+rYd1n/z/QC4bYyhBlhDVXV8krd19z/f4TV/3913v5X3+VSSk7r7gNsOV9Wd1+mCKoCpcoYaYH0dUlWvq6orq+qPq+rbqurPq+qkqnpJkm+tqkvnr7lbVf1pVX24qi6vqqdX1X/KbO7c91bVe5NZCa+q36yqDyf5oX3vt2Hdi+fvcWFVHTlfft/5849U1a9W1d/Plx9VVe+bZ7i8qh6xms0EsFoKNcD6+p4k53T3A5N8Kclz963o7rOS/EN3n9jdP5bkcUn+trsfMj+r/Y7ufnmSv03yyO5+5Pxb75bkA/PXbZ6y6m5JLuzuhyR5X5L/OF/+siQv6+4HJ7l2w+t/NMk7u/vEJA9Jcumi/scBpkShBlhfn+7u/zN//IdJ/vUOr/1IkkdX1Uur6hHd/cVtXndzkjdvs+6fkrxt/viSJMfPH/9Qkv85f/z6Da+/KMnpVfWiJA/u7i/vkA/gdkuhBlhfmy9y2fail+7+eJKHZlasf7Wqfmmbl351h3HTX+tbLqy5ObdyN93ufl+Sf5PZDS5eU1XP3On1ALdXCjXA+jquqn5o/vhHk2weovG1qrpLklTVfZJ8pbv/MMmvZ1auk+TLSe4xmOPCJP9+/vjUfQur6ruS3NDdv5fk9zf8NwHuUBRqgPX1sSTPq6orkxya5BWb1p+b5LKqel2SByf5YFVdmuSFSX51w2vese+ixNvozCQ/U1WXJblfkn3DSX44yYer6q+SPD2zsdYAdzimzQNgR/N5rP+hu7uqTk3yjO4+ZdW5ANbFjuPjACDJ9yf53aqqJDclefZq4wCsF2eoAQBggDHUAAAwQKEGAIABCjUAAAxQqAEAYIBCDQAAAxRqAAAY8P8Bk7oi0tN3GCAAAAAASUVORK5CYII=\n",
-      "text/plain": [
-       "<Figure size 864x432 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "plot_distribution(example_dict);"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## 3. Optimization "
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We estimate the cost of a sampled state vector by making an average over the samples. Determining the cost of a given bitstring takes polynomial time. The average estimate is then used in the classical loop to optimize the variational parameters $\\tau$ and $t$."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 35,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def get_cost_colouring(bitstring, G, penalty=10):\n",
-    "    z = np.array(list(bitstring), dtype=int)\n",
-    "    A = np.array(G.get_adjacency().data)\n",
-    "    # Add penalty and bias:\n",
-    "    cost = penalty*(z.T @ np.triu(A) @ z) - np.sum(z)\n",
-    "    return cost \n",
-    "\n",
-    "def get_cost(res,G):\n",
-    "    counter = dict(res)\n",
-    "    cost = sum(counter[key] * get_cost_colouring(key,G) for key in counter) \n",
-    "    return cost / sum(counter.values()) # Divide by total samples"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 36,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def func(param,*args):\n",
-    "    G = args[0]\n",
-    "    C = quantum_loop(param)\n",
-    "    cost = get_cost(C,G)\n",
-    "    return cost"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We now use a classical optimizer `minimize` in order to find the best variational parameters. This function takes as arguments `func`, the graph `G`and an initial `x0` point for the simplex in Nelder-Mead minimization. The initial point `x0` was estimated beforehand using the best of many initial points, greatly facilitating the optimization process. "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 38,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "res = minimize(func, \n",
-    "               args=G,\n",
-    "               x0=np.r_[guess['t'], guess['s']],\n",
-    "               method='Nelder-Mead',\n",
-    "               tol=1e-5,\n",
-    "               options = {'maxiter': 60}\n",
-    "              )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "We can now plot the sample that we woud obtain using the variational parameters `res.x`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 39,
-   "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "image/png": "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\n",
-      "text/plain": [
-       "<Figure size 864x432 with 1 Axes>"
-      ]
-     },
-     "metadata": {
-      "needs_background": "light"
-     },
-     "output_type": "display_data"
-    }
-   ],
-   "source": [
-    "count_dict = quantum_loop(res.x)\n",
-    "plot_distribution(count_dict)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "QAOA is capable of finding good variational parameters $\\tau$ and $t$. Now, sampling from this final state $\\psi_{f}$ will return both MISs of the graph with high probability. Note that listing all maximal independent sets of a graph is also NP, and can be used as a subroutine for solving many NP-complete graph problems. "
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.8.6"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}


Test patch:
diff --git a/pulser/tests/test_json.py b/pulser/tests/test_json.py
new file mode 100644
index 0000000..cac0472
--- /dev/null
+++ b/pulser/tests/test_json.py
@@ -0,0 +1,59 @@
+# Copyright 2020 Pulser Development Team
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import json
+from unittest.mock import patch
+
+import numpy as np
+import pytest
+
+from pulser import Sequence, Pulse, Register
+from pulser.devices import Chadoq2
+from pulser._json_coders import PulserEncoder, PulserDecoder
+
+
+def encode(obj):
+    return json.dumps(obj, cls=PulserEncoder)
+
+
+def decode(s):
+    return json.loads(s, cls=PulserDecoder)
+
+
+def encode_decode(obj):
+    return decode(encode(obj))
+
+
+def test_encoder():
+    assert np.all(np.arange(10) == encode_decode(np.arange(10)))
+    assert set(range(5)) == encode_decode(set(range(5)))
+    with pytest.raises(TypeError, match="not JSON serializable"):
+        encode(1j)
+
+
+def test_rare_cases():
+    seq = Sequence(Register.square(4), Chadoq2)
+    var = seq.declare_variable("var")
+    pls = Pulse.ConstantPulse(100, 10, var, 0)
+    s = encode(pls.draw())
+    with pytest.warns(UserWarning, match="not encode a Sequence"):
+        pls_ = Sequence.deserialize(s)
+
+    var._assign(-10)
+    with pytest.raises(ValueError, match="No value assigned"):
+        pls_.build()
+
+    var_ = pls_._variables["var"]
+    var_._assign(-10)
+    with patch('matplotlib.pyplot.show'):
+        pls_.build()
diff --git a/pulser/tests/test_seqbuilder.py b/pulser/tests/test_paramseq.py
similarity index 62%
rename from pulser/tests/test_seqbuilder.py
rename to pulser/tests/test_paramseq.py
index 5f5eedf..715f335 100644
--- a/pulser/tests/test_seqbuilder.py
+++ b/pulser/tests/test_paramseq.py
@@ -13,41 +13,24 @@
 # limitations under the License.
 
 import copy
+
+import numpy as np
 import pytest
 
-from pulser import SequenceBuilder, Register, Pulse
-from pulser.devices import Chadoq2
+from pulser import Sequence, Register, Pulse
+from pulser.devices import Chadoq2, MockDevice
 from pulser.parametrized import Variable
-from pulser.seqbuilder import _store
+from pulser.waveforms import BlackmanWaveform
 
 reg = Register.rectangle(4, 3)
 device = Chadoq2
 
 
-def test_store():
-    with pytest.raises(AttributeError, match="not in Sequence"):
-        @_store
-        def f(self, a, b):
-            pass
-
-
-def test_properties():
-    sb = SequenceBuilder(reg, device)
-    assert sb.qubit_info.keys() == set(range(12))
-    assert sb.declared_channels == {}
-    assert sb.available_channels.keys() == Chadoq2.channels.keys()
-    sb.declare_channel("ch1", "rydberg_local")
-    assert sb.declared_channels == {"ch1": Chadoq2.channels["rydberg_local"]}
-    assert sb.available_channels.keys() == (Chadoq2.channels.keys()
-                                            - {"rydberg_local"})
+def test_var_declarations():
+    sb = Sequence(reg, device)
     assert sb.declared_variables == {}
     var = sb.declare_variable("var")
     assert sb.declared_variables == {"var": var}
-
-
-def test_declarations():
-    sb = SequenceBuilder(reg, device)
-    var = sb.declare_variable("var")
     assert isinstance(var, Variable)
     assert var.dtype == float
     assert len(var) == 1
@@ -57,29 +40,30 @@ def test_declarations():
     assert var2.dtype == str
     assert len(var2) == 4
 
-    with pytest.raises(TypeError, match="target has to be a fixed qubit"):
-        sb.declare_channel("ch2", "rydberg_local", var)
-
-    with pytest.raises(ValueError, match="No channel"):
-        sb.declare_channel("ch3", "rydberg", var)
-
 
 def test_stored_calls():
-    sb = SequenceBuilder(reg, device)
+    sb = Sequence(reg, device)
+    assert sb._calls[-1].name == "__init__"
     var = sb.declare_variable("var")
-    sb.declare_channel("ch1", "rydberg_local")
-    assert sb._calls == []
+    assert sb._to_build_calls == []
+    sb.declare_channel("ch1", "rydberg_local", initial_target=var)
+    assert sb._calls[-1].name == "declare_channel"
+    assert sb._to_build_calls[-1].name == "target"
+    assert sb._to_build_calls[-1].args == (var, "ch1")
     with pytest.raises(ValueError, match="name of a declared channel"):
         sb.delay(1000, "rydberg_local")
     x = Variable("x", str)
     var_ = copy.deepcopy(var)
     with pytest.raises(ValueError, match="Unknown variable 'x'"):
         sb.target(x, "ch1")
-    with pytest.raises(ValueError, match="come from this SequenceBuilder"):
+    with pytest.raises(ValueError, match="come from this Sequence"):
         sb.target(var_, "ch1")
 
+    with pytest.raises(ValueError, match="non-variable qubits must belong"):
+        sb.target('q20', "ch1")
+
     sb.delay(var, "ch1")
-    call = sb._calls[0]
+    call = sb._to_build_calls[1]
     assert call.name == "delay"
     assert call.args == (var, "ch1")
     assert call.kwargs == {}
@@ -88,23 +72,44 @@ def test_stored_calls():
 
     with pytest.raises(ValueError, match="Invalid protocol 'last'"):
         sb.add(pls, "ch1", protocol="last")
-    assert sb._calls[-1] == call
+
+    with pytest.raises(ValueError, match='amplitude goes over the maximum'):
+        sb.add(Pulse.ConstantPulse(20, 2*np.pi*100, -2*np.pi*100, 0), 'ch1')
+    with pytest.raises(ValueError,
+                       match='detuning values go out of the range'):
+        sb.add(Pulse.ConstantPulse(500, 2*np.pi, -2*np.pi*100, 0), 'ch1')
+
+    assert sb._to_build_calls[-1] == call
     sb.add(pls, "ch1", protocol="wait-for-all")
-    call = sb._calls[1]
+    call = sb._to_build_calls[2]
     assert call.name == "add"
     assert call.args == (pls, "ch1")
     assert call.kwargs == {"protocol": "wait-for-all"}
 
     q_var = sb.declare_variable("q_var", size=5, dtype=str)
     sb.declare_channel("ch2", "rydberg_global")
+    assert len(sb._calls) == 3
+    assert sb._calls[-1].name == "declare_channel"
     with pytest.raises(ValueError, match="'Local' channels"):
         sb.target(0, "ch2")
     with pytest.raises(ValueError, match="target at most 1 qubits"):
         sb.target(q_var, "ch1")
 
+    sb2 = Sequence(reg, MockDevice)
+    sb2.declare_channel("ch1", "rydberg_local", initial_target={3, 4, 5})
+    q_var2 = sb2.declare_variable("q_var2", size=5, dtype=str)
+    var2 = sb2.declare_variable("var2")
+    assert sb2._building
+    sb2.target({var2, 7, 9, 10}, "ch1")
+    assert not sb2._building
+    sb2.target(q_var2, "ch1")
+
     with pytest.raises(ValueError, match="targets the given 'basis'"):
         sb.phase_shift(var, *q_var)
 
+    with pytest.raises(ValueError, match="non-variable targets must belong"):
+        sb.phase_shift(var, *q_var, "q1", basis="ground-rydberg")
+
     with pytest.raises(ValueError, match="correspond to declared channels"):
         sb.align("ch1", var)
     with pytest.raises(ValueError, match="more than once"):
@@ -121,44 +126,42 @@ def test_stored_calls():
 
 def test_build():
     reg_ = Register.rectangle(2, 1, prefix="q")
-    sb = SequenceBuilder(reg_, device)
+    sb = Sequence(reg_, device)
     var = sb.declare_variable("var")
     targ_var = sb.declare_variable("targ_var", size=2, dtype=str)
     sb.declare_channel("ch1", "rydberg_local")
-    sb.declare_channel("ch2", "raman_local")
+    sb.declare_channel("ch2", "raman_local", initial_target=targ_var[0])
     sb.target(targ_var[1], "ch1")
-    pls = Pulse.ConstantPulse(var*100, var, var, var)
+    wf = BlackmanWaveform(var*100, np.pi)
+    pls = Pulse.ConstantDetuning(wf, var, var)
     sb.add(pls, "ch1")
     sb.delay(var*50, "ch1")
-    sb.target(targ_var[0], "ch2")
     sb.align("ch2", "ch1")
     sb.phase_shift(var, targ_var[0])
+    pls2 = Pulse.ConstantPulse(wf.duration, var, var, 0)
+    sb.add(pls2, "ch2")
     sb.measure()
     with pytest.warns(UserWarning, match="No declared variables"):
         sb.build(t=100, var=2, targ_var=["q1", "q0"])
     with pytest.raises(TypeError, match="Did not receive values for"):
         sb.build(var=2)
     seq = sb.build(var=2, targ_var=["q1", "q0"])
-    assert seq._schedule["ch2"][-1].tf == 300
+    assert seq._schedule["ch2"][-1].tf == 500
     assert seq.current_phase_ref("q1") == 2.0
     assert seq.current_phase_ref("q0") == 0.
     assert seq._measurement == "ground-rydberg"
 
-
-def test_getattr():
-    sb = SequenceBuilder(reg, device)
-    with pytest.raises(AttributeError,
-                       match="attribute of 'Sequence' is not available"):
-        sb.draw()
-    with pytest.raises(AttributeError, match="has no attribute"):
-        sb.dra()
+    s = sb.serialize()
+    sb_ = Sequence.deserialize(s)
+    assert str(sb) == str(sb_)
 
 
 def test_str():
     reg_ = Register.rectangle(2, 1, prefix="q")
-    sb = SequenceBuilder(reg_, device)
+    sb = Sequence(reg_, device)
     sb.declare_channel("ch1", "rydberg_global")
-    seq = sb.build()
+    with pytest.warns(UserWarning, match="Building a non-parametrized"):
+        seq = sb.build()
     var = sb.declare_variable("var")
     pls = Pulse.ConstantPulse(var*100, var, -1, var)
     sb.add(pls, "ch1")
@@ -166,3 +169,13 @@ def test_str():
          + "1. add(Pulse(ConstantWaveform(mul(var, 100), var), "
          + "ConstantWaveform(mul(var, 100), -1), var, 0), ch1)")
     assert s == str(sb)
+
+
+def test_screen():
+    sb = Sequence(reg, device)
+    sb.declare_channel("ch1", "rydberg_global")
+    assert sb.current_phase_ref(4, basis="ground-rydberg") == 0
+    var = sb.declare_variable("var")
+    sb.delay(var, "ch1")
+    with pytest.raises(RuntimeError, match="can't be called in parametrized"):
+        sb.current_phase_ref(4, basis="ground-rydberg")
diff --git a/pulser/tests/test_sequence.py b/pulser/tests/test_sequence.py
index 3d4e84d..a37d712 100644
--- a/pulser/tests/test_sequence.py
+++ b/pulser/tests/test_sequence.py
@@ -11,12 +11,13 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+import json
 from unittest.mock import patch
 
 import numpy as np
 import pytest
 
+import pulser
 from pulser import Sequence, Pulse, Register
 from pulser.devices import Chadoq2, MockDevice
 from pulser.devices._pasqal_device import PasqalDevice
@@ -69,7 +70,7 @@ def test_target():
 
     with pytest.raises(ValueError, match='name of a declared channel'):
         seq.target('q0', 'ch2')
-    with pytest.raises(ValueError, match='qubits have to belong'):
+    with pytest.raises(ValueError, match='qubits must belong'):
         seq.target(0, 'ch0')
         seq.target('0', 'ch1')
     with pytest.raises(ValueError, match="Can only choose target of 'Local'"):
@@ -253,3 +254,8 @@ def test_sequence():
 
     with patch('matplotlib.pyplot.show'):
         seq.draw()
+
+    s = seq.serialize()
+    assert json.loads(s)["__version__"] == pulser.__version__
+    seq_ = Sequence.deserialize(s)
+    assert str(seq) == str(seq_)
diff --git a/pulser/tests/test_waveforms.py b/pulser/tests/test_waveforms.py
index 51e6997..87b3616 100644
--- a/pulser/tests/test_waveforms.py
+++ b/pulser/tests/test_waveforms.py
@@ -12,11 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import json
 from unittest.mock import patch
 
 import numpy as np
 import pytest
 
+from pulser._json_coders import PulserEncoder, PulserDecoder
 from pulser.waveforms import (ConstantWaveform, RampWaveform, BlackmanWaveform,
                               CustomWaveform, CompositeWaveform)
 
@@ -160,3 +162,9 @@ def test_ops():
     assert composite * 1 == composite
     with pytest.raises(ZeroDivisionError):
         constant / 0
+
+
+def test_serialization():
+    for wf in [constant, ramp, custom, blackman, composite]:
+        s = json.dumps(wf, cls=PulserEncoder)
+        assert wf == json.loads(s, cls=PulserDecoder)