示例#1
0
    def __init__(self,
                 *subtemplates: Union[AtomicPulseTemplate, MappingTuple, MappingPulseTemplate],
                 identifier: Optional[str] = None,
                 parameter_constraints: Optional[List] = None,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 registry: PulseRegistryType = None,
                 duration: Optional[ExpressionLike] = None) -> None:
        """Parallels multiple AtomicPulseTemplates of the same duration. If the duration keyword argument is given
        it is enforced that the instantiated pulse template has this duration. If duration is None the duration of the
        PT is the duration of the first subtemplate. There are probably changes to this behaviour in the future.

        Args:
            *subtemplates: Positional arguments are subtemplates to combine.
            identifier: Forwarded to AtomicPulseTemplate.__init__
            parameter_constraints: Forwarded to ParameterConstrainer.__init__
            measurements: Forwarded to AtomicPulseTemplate.__init__
            duration: Enforced duration of the pulse template on instantiation. build_waveform checks all sub-waveforms
            have this duration. If True the equality of durations is only checked durtin instantiation not construction.
        """
        AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements)
        ParameterConstrainer.__init__(self, parameter_constraints=parameter_constraints)

        self._subtemplates = [st if isinstance(st, PulseTemplate) else MappingPulseTemplate.from_tuple(st) for st in
                              subtemplates]

        if duration in (True, False):
            warnings.warn("Boolean duration is deprecated since qupulse 0.6 and interpreted as None",
                          category=DeprecationWarning, stacklevel=2)
            duration = None

        for subtemplate in self._subtemplates:
            if isinstance(subtemplate, AtomicPulseTemplate):
                continue
            elif isinstance(subtemplate, MappingPulseTemplate):
                if isinstance(subtemplate.template, AtomicPulseTemplate):
                    continue
                else:
                    raise TypeError('Non atomic subtemplate of MappingPulseTemplate: {}'.format(subtemplate.template))
            else:
                raise TypeError('Non atomic subtemplate: {}'.format(subtemplate))

        if not self._subtemplates:
            raise ValueError('Cannot create empty MultiChannelPulseTemplate')

        defined_channels = [st.defined_channels for st in self._subtemplates]

        # check there are no intersections between channels
        for i, channels_i in enumerate(defined_channels):
            for j, channels_j in enumerate(defined_channels[i + 1:]):
                if channels_i & channels_j:
                    raise ChannelMappingException('subtemplate {}'.format(i + 1),
                                                  'subtemplate {}'.format(i + 2 + j),
                                                  (channels_i & channels_j).pop())

        if duration is None:
            self._duration = None
        else:
            self._duration = ExpressionScalar(duration)

        self._register(registry=registry)
示例#2
0
    def __init__(self,
                 body: PulseTemplate,
                 loop_index: str,
                 loop_range: Union[int, range, str, Tuple[Any, Any],
                                   Tuple[Any, Any, Any], ParametrizedRange],
                 identifier: Optional[str] = None,
                 *,
                 measurements: Optional[
                     Sequence[MeasurementDeclaration]] = None,
                 parameter_constraints: Optional[Sequence] = None,
                 registry: PulseRegistryType = None) -> None:
        """
        Args:
            body: The loop body. It is expected to have `loop_index` as an parameter
            loop_index: Loop index of the for loop
            loop_range: Range to loop through
            identifier: Used for serialization
        """
        LoopPulseTemplate.__init__(self, body=body, identifier=identifier)
        MeasurementDefiner.__init__(self, measurements=measurements)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        if isinstance(loop_range, ParametrizedRange):
            self._loop_range = loop_range
        elif isinstance(loop_range, (int, str)):
            self._loop_range = ParametrizedRange(loop_range)
        elif isinstance(loop_range, (tuple, list)):
            self._loop_range = ParametrizedRange(*loop_range)
        elif isinstance(loop_range, range):
            self._loop_range = ParametrizedRange(start=loop_range.start,
                                                 stop=loop_range.stop,
                                                 step=loop_range.step)
        else:
            raise ValueError('loop_range is not valid')

        if not loop_index.isidentifier():
            raise InvalidParameterNameException(loop_index)
        body_parameters = self.body.parameter_names
        if loop_index not in body_parameters:
            raise LoopIndexNotUsedException(loop_index, body_parameters)
        self._loop_index = loop_index

        if self.loop_index in self.constrained_parameters:
            constraints = [
                str(constraint) for constraint in self.parameter_constraints
                if self._loop_index in constraint.affected_parameters
            ]
            warnings.warn("ForLoopPulseTemplate was created with a constraint on a variable shadowing the loop index.\n" \
                          "This will not constrain the actual loop index but introduce a new parameter.\n" \
                          "To constrain the loop index, put the constraint in the body subtemplate.\n" \
                          "Loop index is {} and offending constraints are: {}".format(self._loop_index, constraints))

        self._register(registry=registry)
示例#3
0
    def __init__(self,
                 *subtemplates: Union[PulseTemplate, MappingTuple],
                 external_parameters: Optional[Union[Iterable[str],
                                                     Set[str]]] = None,
                 identifier: Optional[str] = None,
                 parameter_constraints: Optional[List[Union[
                     str, Expression]]] = None,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 registry: PulseRegistryType = None) -> None:
        """Create a new SequencePulseTemplate instance.

        Requires a (correctly ordered) list of subtemplates in the form
        (PulseTemplate, Dict(str -> str)) where the dictionary is a mapping between the external
        parameters exposed by this SequencePulseTemplate to the parameters declared by the
        subtemplates, specifying how the latter are derived from the former, i.e., the mapping is
        subtemplate_parameter_name -> mapping_expression (as str) where the free variables in the
        mapping_expression are parameters declared by this SequencePulseTemplate.

        The following requirements must be satisfied:
            - for each parameter declared by a subtemplate, a mapping expression must be provided
            - each free variable in a mapping expression must be declared as an external parameter
                of this SequencePulseTemplate

        Args:
            subtemplates (List(Subtemplate)): The list of subtemplates of this
                SequencePulseTemplate as tuples of the form (PulseTemplate, Dict(str -> str)).
            external_parameters (List(str)): A set of names for external parameters of this
                SequencePulseTemplate. Deprecated.
            identifier (str): A unique identifier for use in serialization. (optional)
        """
        PulseTemplate.__init__(self, identifier=identifier)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)
        MeasurementDefiner.__init__(self, measurements=measurements)

        self.__subtemplates = [
            MappingPulseTemplate.from_tuple(st)
            if isinstance(st, tuple) else st for st in subtemplates
        ]

        # check that all subtemplates live on the same channels
        defined_channels = self.__subtemplates[0].defined_channels
        for subtemplate in self.__subtemplates[1:]:
            if subtemplate.defined_channels != defined_channels:
                raise ValueError(
                    'The subtemplates are defined for different channels')

        if external_parameters:
            warnings.warn(
                "external_parameters is an obsolete argument and will be removed in the future.",
                category=DeprecationWarning)

        self._register(registry=registry)
示例#4
0
    def __init__(self,
                 time_point_tuple_list: List[EntryInInit],
                 channel_names: Sequence[ChannelID],
                 *,
                 parameter_constraints: Optional[List[Union[str, ParameterConstraint]]]=None,
                 measurements: Optional[List[MeasurementDeclaration]]=None,
                 identifier: Optional[str]=None,
                 registry: PulseRegistryType=None) -> None:

        AtomicPulseTemplate.__init__(self, identifier=identifier, measurements=measurements)
        ParameterConstrainer.__init__(self, parameter_constraints=parameter_constraints)

        self._channels = tuple(channel_names)
        self._entries = [PointPulseEntry(*tpt)
                         for tpt in time_point_tuple_list]

        self._register(registry=registry)
示例#5
0
    def __init__(self,
                 body: PulseTemplate,
                 repetition_count: Union[int, str, ExpressionScalar],
                 identifier: Optional[str] = None,
                 *args,
                 parameter_constraints: Optional[List] = None,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 registry: PulseRegistryType = None) -> None:
        """Create a new RepetitionPulseTemplate instance.

        Args:
            body (PulseTemplate): The PulseTemplate which will be repeated.
            repetition_count (int or ParameterDeclaration): The number of repetitions either as a
                constant integer value or as a parameter declaration.
            identifier (str): A unique identifier for use in serialization. (optional)
        """
        if len(args) == 1 and parameter_constraints is None:
            warn(
                'You used parameter_constraints as a positional argument. It will be keyword only in a future version.',
                DeprecationWarning)
        elif args:
            TypeError(
                'RepetitionPulseTemplate expects 3 positional arguments, got '
                + str(3 + len(args)))

        LoopPulseTemplate.__init__(self, identifier=identifier, body=body)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)
        MeasurementDefiner.__init__(self, measurements=measurements)

        repetition_count = ExpressionScalar.make(repetition_count)

        if repetition_count < 0:
            raise ValueError('Repetition count may not be negative')

        if repetition_count == 0:
            warn(
                "Repetition pulse template with 0 repetitions on construction."
            )

        self._repetition_count = repetition_count

        self._register(registry=registry)
示例#6
0
    def __init__(self,
                 expression: Union[str, ExpressionScalar],
                 duration_expression: Union[str, ExpressionScalar],
                 channel: ChannelID = 'default',
                 identifier: Optional[str] = None,
                 *,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 parameter_constraints: Optional[List[Union[
                     str, ParameterConstraint]]] = None,
                 registry: PulseRegistryType = None) -> None:
        """Creates a new FunctionPulseTemplate object.

        Args:
            expression: The function represented by this FunctionPulseTemplate
                as a mathematical expression where 't' denotes the time variable and other variables
                will be parameters of the pulse.
            duration_expression: A mathematical expression which reliably
                computes the duration of an instantiation of this FunctionPulseTemplate from
                provided parameter values.
            channel: The channel this pulse template is defined on.
            identifier: A unique identifier for use in serialization.
            measurements: A list of measurement declarations forwarded to the
                :class:`~qupulse.pulses.measurement.MeasurementDefiner` superclass
            parameter_constraints: A list of parameter constraints forwarded to the
                :class:`~qupulse.pulses.measurement.ParameterConstrainer` superclass
        """
        AtomicPulseTemplate.__init__(self,
                                     identifier=identifier,
                                     measurements=measurements)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        self.__expression = ExpressionScalar.make(expression)
        self.__duration_expression = ExpressionScalar.make(duration_expression)
        self.__parameter_names = {
            *self.__duration_expression.variables, *self.__expression.variables
        } - {'t'}
        self.__channel = channel

        self._register(registry=registry)
    def __init__(self,
                 *subtemplates: Union[AtomicPulseTemplate, MappingTuple,
                                      MappingPulseTemplate],
                 external_parameters: Optional[Set[str]] = None,
                 identifier: Optional[str] = None,
                 parameter_constraints: Optional[List] = None,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 registry: PulseRegistryType = None,
                 duration: Union[str, Expression, bool] = False) -> None:
        """Parallels multiple AtomicPulseTemplates of the same duration. The duration equality check is performed on
        construction by default. If the duration keyword argument is given the check is performed on instantiation
        (when build_waveform is called). duration can be a Expression to enforce a certain duration or True for an
        unspecified duration.

        Args:
            *subtemplates: Positional arguments are subtemplates to combine.
            identifier: Forwarded to AtomicPulseTemplate.__init__
            parameter_constraints: Forwarded to ParameterConstrainer.__init__
            measurements: Forwarded to AtomicPulseTemplate.__init__
            duration: Enforced duration of the pulse template on instantiation. build_waveform checks all sub-waveforms
            have this duration. If True the equality of durations is only checked durtin instantiation not construction.
            external_parameters: No functionality. (Deprecated)
        """
        AtomicPulseTemplate.__init__(self,
                                     identifier=identifier,
                                     measurements=measurements)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        self._subtemplates = [
            st if isinstance(st, PulseTemplate) else
            MappingPulseTemplate.from_tuple(st) for st in subtemplates
        ]

        for subtemplate in self._subtemplates:
            if isinstance(subtemplate, AtomicPulseTemplate):
                continue
            elif isinstance(subtemplate, MappingPulseTemplate):
                if isinstance(subtemplate.template, AtomicPulseTemplate):
                    continue
                else:
                    raise TypeError(
                        'Non atomic subtemplate of MappingPulseTemplate: {}'.
                        format(subtemplate.template))
            else:
                raise TypeError(
                    'Non atomic subtemplate: {}'.format(subtemplate))

        if not self._subtemplates:
            raise ValueError('Cannot create empty MultiChannelPulseTemplate')

        defined_channels = [st.defined_channels for st in self._subtemplates]

        # check there are no intersections between channels
        for i, channels_i in enumerate(defined_channels):
            for j, channels_j in enumerate(defined_channels[i + 1:]):
                if channels_i & channels_j:
                    raise ChannelMappingException(
                        'subtemplate {}'.format(i + 1),
                        'subtemplate {}'.format(i + 2 + j),
                        (channels_i | channels_j).pop())

        if external_parameters is not None:
            warnings.warn(
                "external_parameters is an obsolete argument and will be removed in the future.",
                category=DeprecationWarning)

        if not duration:
            duration = self._subtemplates[0].duration
            for subtemplate in self._subtemplates[1:]:
                if almost_equal(duration.sympified_expression,
                                subtemplate.duration.sympified_expression):
                    continue
                else:
                    raise ValueError(
                        'Could not assert duration equality of {} and {}'.
                        format(duration, subtemplate.duration))
            self._duration = None
        elif duration is True:
            self._duration = None
        else:
            self._duration = ExpressionScalar(duration)

        self._register(registry=registry)
示例#8
0
    def __init__(self,
                 entries: Dict[ChannelID, Sequence[EntryInInit]],
                 identifier: Optional[str] = None,
                 *,
                 parameter_constraints: Optional[List[Union[
                     str, ParameterConstraint]]] = None,
                 measurements: Optional[List[MeasurementDeclaration]] = None,
                 consistency_check: bool = True,
                 registry: PulseRegistryType = None) -> None:
        """
        Construct a `TablePulseTemplate` from a dict which maps channels to their entries. By default the consistency
        of the provided entries is checked. There are two static functions for convenience construction: from_array and
        from_entry_list.

        Args:
            entries: A dictionary that maps channel ids to a list of entries. An entry is a
                (time, voltage[, interpolation strategy]) tuple or a TableEntry
            identifier: Used for serialization
            parameter_constraints: Constraint list that is forwarded to the ParameterConstrainer superclass
            measurements: Measurement declaration list that is forwarded to the MeasurementDefiner superclass
            consistency_check: If True the consistency of the times will be checked on construction as far as possible
        """
        AtomicPulseTemplate.__init__(self,
                                     identifier=identifier,
                                     measurements=measurements)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        if not entries:
            raise ValueError(
                "Cannot construct an empty TablePulseTemplate (no entries given). There is currently no "
                "specific reason for this. Please submit an issue if you need this 'feature'."
            )

        self._entries = dict((ch, list()) for ch in entries.keys())
        for channel, channel_entries in entries.items():
            if len(channel_entries) == 0:
                raise ValueError('Channel {} is empty'.format(channel))

            for entry in channel_entries:
                self._add_entry(channel, TableEntry(*entry))

        self._duration = self.calculate_duration()
        self._table_parameters = set(
            var for channel_entries in self.entries.values()
            for entry in channel_entries
            for var in itertools.chain(entry.t.variables, entry.v.variables
                                       )) | self.constrained_parameters

        if self.duration == 0:
            warnings.warn(
                'Table pulse template with duration 0 on construction.',
                category=ZeroDurationTablePulseTemplate)

        if consistency_check:
            # perform a simple consistency check. All inequalities with more than one free variable are ignored as the
            # sympy solver does not support them

            # collect all conditions
            inequalities = [eq.sympified_expression for eq in self._parameter_constraints] +\
                           [sympy.Le(previous_entry.t.underlying_expression, entry.t.underlying_expression)
                            for channel_entries in self._entries.values()
                            for previous_entry, entry in zip(channel_entries, channel_entries[1:])]

            # test if any condition is already dissatisfied
            if any(
                    isinstance(eq, BooleanAtom) and bool(eq) is False
                    for eq in inequalities):
                raise ValueError(
                    'Table pulse template has impossible parametrization')

            # filter conditions that are inequalities with one free variable and test if the solution set is empty
            inequalities = [
                eq for eq in inequalities
                if isinstance(eq, sympy.Rel) and len(eq.free_symbols) == 1
            ]
            if not sympy.reduce_inequalities(inequalities):
                raise ValueError(
                    'Table pulse template has impossible parametrization')

        self._register(registry=registry)
示例#9
0
    def __init__(self,
                 template: PulseTemplate,
                 *,
                 identifier: Optional[str] = None,
                 parameter_mapping: Optional[Dict[str, str]] = None,
                 measurement_mapping: Optional[Dict[str, str]] = None,
                 channel_mapping: Optional[Dict[ChannelID, ChannelID]] = None,
                 parameter_constraints: Optional[List[str]] = None,
                 allow_partial_parameter_mapping: bool = None,
                 registry: PulseRegistryType = None) -> None:
        """Standard constructor for the MappingPulseTemplate.

        Mappings that are not specified are defaulted to identity mappings. Channels and measurement names of the
        encapsulated template can be mapped partially by default. F.i. if channel_mapping only contains one of two
        channels the other channel name is mapped to itself. Channels that are mapped to None are dropped.
        However, if a parameter mapping is specified and one or more parameters are not mapped a MissingMappingException
        is raised. To allow partial mappings and enable the same behaviour as for the channel and measurement name
        mapping allow_partial_parameter_mapping must be set to True.
        Furthermore parameter constrains can be specified.
        
        :param template: The encapsulated pulse template whose parameters, measurement names and channels are mapped
        :param parameter_mapping: if not none, mappings for all parameters must be specified
        :param measurement_mapping: mappings for other measurement names are inserted
        :param channel_mapping: mappings for other channels are auto inserted. Mapping to None drops the channel.
        :param parameter_constraints:
        :param allow_partial_parameter_mapping: If None the value of the class variable ALLOW_PARTIAL_PARAMETER_MAPPING
        """
        PulseTemplate.__init__(self, identifier=identifier)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        if allow_partial_parameter_mapping is None:
            allow_partial_parameter_mapping = self.ALLOW_PARTIAL_PARAMETER_MAPPING

        if parameter_mapping is None:
            parameter_mapping = dict(
                (par, par) for par in template.parameter_names)
        else:
            mapped_internal_parameters = set(parameter_mapping.keys())
            internal_parameters = template.parameter_names
            missing_parameter_mappings = internal_parameters - mapped_internal_parameters
            if mapped_internal_parameters - internal_parameters:
                raise UnnecessaryMappingException(
                    template, mapped_internal_parameters - internal_parameters)
            elif missing_parameter_mappings:
                if allow_partial_parameter_mapping:
                    parameter_mapping.update(
                        {p: p
                         for p in missing_parameter_mappings})
                else:
                    raise MissingMappingException(
                        template,
                        internal_parameters - mapped_internal_parameters)
        parameter_mapping = dict(
            (k, Expression(v)) for k, v in parameter_mapping.items())

        measurement_mapping = dict(
        ) if measurement_mapping is None else measurement_mapping
        internal_names = template.measurement_names
        mapped_internal_names = set(measurement_mapping.keys())
        if mapped_internal_names - internal_names:
            raise UnnecessaryMappingException(
                template, mapped_internal_names - internal_names)
        missing_name_mappings = internal_names - mapped_internal_names
        measurement_mapping = dict(
            itertools.chain(((name, name) for name in missing_name_mappings),
                            measurement_mapping.items()))

        # we copy to modify in place
        channel_mapping = dict(
        ) if channel_mapping is None else channel_mapping.copy()
        internal_channels = template.defined_channels
        mapped_internal_channels = set(channel_mapping.keys())
        if mapped_internal_channels - internal_channels:
            raise UnnecessaryMappingException(
                template, mapped_internal_channels - internal_channels)

        # fill up implicit mappings (unchanged channels)
        missing_channel_mappings = internal_channels - mapped_internal_channels
        for name in missing_channel_mappings:
            channel_mapping[name] = name

        # None is an allowed overlapping target as it marks dropped channels
        overlapping_targets = {
            channel
            for channel, n in collections.Counter(
                channel_mapping.values()).items()
            if n > 1 and channel is not None
        }
        if overlapping_targets:
            raise ValueError(
                'Cannot map multiple channels to the same target(s) %r' %
                overlapping_targets, channel_mapping)

        if isinstance(template,
                      MappingPulseTemplate) and template.identifier is None:
            # avoid nested mappings
            parameter_mapping = {
                p: Expression(expr.evaluate_symbolic(parameter_mapping))
                for p, expr in template.parameter_mapping.items()
            }
            measurement_mapping = {
                k: measurement_mapping[v]
                for k, v in template.measurement_mapping.items()
            }
            channel_mapping = {
                k: channel_mapping[v]
                for k, v in template.channel_mapping.items()
            }
            template = template.template

        self.__template = template
        self.__parameter_mapping = FrozenDict(parameter_mapping)
        self.__external_parameters = set(
            itertools.chain(*(expr.variables
                              for expr in self.__parameter_mapping.values())))
        self.__external_parameters |= self.constrained_parameters
        self.__measurement_mapping = measurement_mapping
        self.__channel_mapping = channel_mapping
        self._register(registry=registry)
示例#10
0
    def __init__(self,
                 template: PulseTemplate,
                 *,
                 identifier: Optional[str] = None,
                 parameter_mapping: Optional[Dict[str, str]] = None,
                 measurement_mapping: Optional[Dict[str, str]] = None,
                 channel_mapping: Optional[Dict[ChannelID, ChannelID]] = None,
                 parameter_constraints: Optional[List[str]] = None,
                 allow_partial_parameter_mapping: bool = False,
                 registry: PulseRegistryType = None) -> None:
        """Standard constructor for the MappingPulseTemplate.

        Mappings that are not specified are defaulted to identity mappings. Channels and measurement names of the
        encapsulated template can be mapped partially by default. F.i. if channel_mapping only contains one of two
        channels the other channel name is mapped to itself.
        However, if a parameter mapping is specified and one or more parameters are not mapped a MissingMappingException
        is raised. To allow partial mappings and enable the same behaviour as for the channel and measurement name
        mapping allow_partial_parameter_mapping must be set to True.
        Furthermore parameter constrains can be specified.
        
        :param template: The encapsulated pulse template whose parameters, measurement names and channels are mapped
        :param parameter_mapping: if not none, mappings for all parameters must be specified
        :param measurement_mapping: mappings for other measurement names are inserted
        :param channel_mapping: mappings for other channels are auto inserted
        :param parameter_constraints:
        :param allow_partial_parameter_mapping:
        """
        PulseTemplate.__init__(self, identifier=identifier)
        ParameterConstrainer.__init__(
            self, parameter_constraints=parameter_constraints)

        if parameter_mapping is None:
            parameter_mapping = dict(
                (par, par) for par in template.parameter_names)
        else:
            mapped_internal_parameters = set(parameter_mapping.keys())
            internal_parameters = template.parameter_names
            missing_parameter_mappings = internal_parameters - mapped_internal_parameters
            if mapped_internal_parameters - internal_parameters:
                raise UnnecessaryMappingException(
                    template, mapped_internal_parameters - internal_parameters)
            elif missing_parameter_mappings:
                if allow_partial_parameter_mapping:
                    parameter_mapping.update(
                        {p: p
                         for p in missing_parameter_mappings})
                else:
                    raise MissingMappingException(
                        template,
                        internal_parameters - mapped_internal_parameters)
        parameter_mapping = dict(
            (k, Expression(v)) for k, v in parameter_mapping.items())

        measurement_mapping = dict(
        ) if measurement_mapping is None else measurement_mapping
        internal_names = template.measurement_names
        mapped_internal_names = set(measurement_mapping.keys())
        if mapped_internal_names - internal_names:
            raise UnnecessaryMappingException(
                template, mapped_internal_names - internal_names)
        missing_name_mappings = internal_names - mapped_internal_names
        measurement_mapping = dict(
            itertools.chain(((name, name) for name in missing_name_mappings),
                            measurement_mapping.items()))

        channel_mapping = dict(
        ) if channel_mapping is None else channel_mapping
        internal_channels = template.defined_channels
        mapped_internal_channels = set(channel_mapping.keys())
        if mapped_internal_channels - internal_channels:
            raise UnnecessaryMappingException(
                template, mapped_internal_channels - internal_channels)
        missing_channel_mappings = internal_channels - mapped_internal_channels
        channel_mapping = dict(
            itertools.chain(
                ((name, name) for name in missing_channel_mappings),
                channel_mapping.items()))

        if isinstance(template,
                      MappingPulseTemplate) and template.identifier is None:
            # avoid nested mappings
            parameter_mapping = {
                p: Expression(expr.evaluate_symbolic(parameter_mapping))
                for p, expr in template.parameter_mapping.items()
            }
            measurement_mapping = {
                k: measurement_mapping[v]
                for k, v in template.measurement_mapping.items()
            }
            channel_mapping = {
                k: channel_mapping[v]
                for k, v in template.channel_mapping.items()
            }
            template = template.template

        self.__template = template
        self.__parameter_mapping = parameter_mapping
        self.__external_parameters = set(
            itertools.chain(*(expr.variables
                              for expr in self.__parameter_mapping.values())))
        self.__external_parameters |= self.constrained_parameters
        self.__measurement_mapping = measurement_mapping
        self.__channel_mapping = channel_mapping
        self._register(registry=registry)