def __init__(self,
                 lhs: Union[PulseTemplate, ExpressionLike,
                            Mapping[ChannelID, ExpressionLike]],
                 arithmetic_operator: str,
                 rhs: Union[PulseTemplate, ExpressionLike,
                            Mapping[ChannelID, ExpressionLike]],
                 *,
                 identifier: Optional[str] = None):
        """

        Args:
            lhs: Left hand side operand
            arithmetic_operator: String representation of the operator
            rhs: Right hand side operand
            identifier:

        Raises:
             TypeError if both or none of the  operands are pulse templates
        """
        PulseTemplate.__init__(self, identifier=identifier)

        if not isinstance(lhs, PulseTemplate) and not isinstance(
                rhs, PulseTemplate):
            raise TypeError(
                'At least one of the operands needs to be a pulse template.')

        elif not isinstance(lhs, PulseTemplate) and isinstance(
                rhs, PulseTemplate):
            #  +, - and * with (scalar, PT)
            if arithmetic_operator not in ('+', '-', '*'):
                raise ValueError(
                    'Operands (scalar, PulseTemplate) require an operator from {+, -, *}'
                )
            scalar = lhs = self._parse_operand(lhs, rhs.defined_channels)
            pulse_template = rhs

        elif isinstance(lhs,
                        PulseTemplate) and not isinstance(rhs, PulseTemplate):
            #  +, -, *, / and // with (PT, scalar)
            if arithmetic_operator not in ('+', '-', '*', '/'):
                raise ValueError(
                    'Operands (PulseTemplate, scalar) require an operator from {+, -, *, /}'
                )
            scalar = rhs = self._parse_operand(rhs, lhs.defined_channels)
            pulse_template = lhs

        else:
            # + and - with (AtomicPulseTemplate, AtomicPulseTemplate) as operands
            raise TypeError(
                'ArithmeticPulseTemplate cannot combine two PulseTemplates')

        self._lhs = lhs
        self._rhs = rhs

        self._pulse_template = pulse_template
        self._scalar = scalar

        self._arithmetic_operator = arithmetic_operator
예제 #2
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)
예제 #3
0
def plot(pulse: PulseTemplate,
         parameters: Dict[str, Parameter] = None,
         sample_rate: Real = 10,
         axes: Any = None,
         show: bool = True,
         plot_channels: Optional[Set[ChannelID]] = None,
         plot_measurements: Optional[Set[str]] = None,
         stepped: bool = True,
         maximum_points: int = 10**6,
         time_slice: Tuple[Real, Real] = None,
         **kwargs) -> Any:  # pragma: no cover
    """Plots a pulse using matplotlib.

    The given pulse template will first be turned into a pulse program (represented by a Loop object) with the provided
    parameters. The render() function is then invoked to obtain voltage samples over the entire duration of the pulse which
    are then plotted in a matplotlib figure.

    Args:
        pulse: The pulse to be plotted.
        parameters: An optional mapping of parameter names to Parameter
            objects.
        sample_rate: The rate with which the waveforms are sampled for the plot in
            samples per time unit. (default = 10)
        axes: matplotlib Axes object the pulse will be drawn into if provided
        show: If true, the figure will be shown
        plot_channels: If specified only channels from this set will be plotted. If omitted all channels will be.
        stepped: If true pyplot.step is used for plotting
        plot_measurements: If specified measurements in this set will be plotted. If omitted no measurements will be.
        maximum_points: If the sampled waveform is bigger, it is not plotted
        time_slice: The time slice to be plotted. If None, the entire pulse will be shown.
        kwargs: Forwarded to pyplot. Overwrites other settings.
    Returns:
        matplotlib.pyplot.Figure instance in which the pulse is rendered
    Raises:
        PlottingNotPossibleException if the sequencing is interrupted before it finishes, e.g.,
            because a parameter value could not be evaluated
        all Exceptions possibly raised during sequencing
    """
    from matplotlib import pyplot as plt

    channels = pulse.defined_channels

    if parameters is None:
        parameters = dict()

    program = pulse.create_program(
        parameters=parameters,
        channel_mapping={ch: ch
                         for ch in channels},
        measurement_mapping={w: w
                             for w in pulse.measurement_names})

    if program is not None:
        times, voltages, measurements = render(
            program,
            sample_rate,
            render_measurements=bool(plot_measurements),
            time_slice=time_slice)
    else:
        times, voltages, measurements = np.array([]), dict(), []

    duration = 0
    if times.size == 0:
        warnings.warn("Pulse to be plotted is empty!")
    elif times.size > maximum_points:
        # todo [2018-05-30]: since it results in an empty return value this should arguably be an exception, not just a warning
        warnings.warn(
            "Sampled pulse of size {wf_len} is lager than {max_points}".format(
                wf_len=times.size, max_points=maximum_points))
        return None
    else:
        duration = times[-1]

    if time_slice is None:
        time_slice = (0, duration)

    legend_handles = []
    if axes is None:
        # plot to figure
        figure = plt.figure()
        axes = figure.add_subplot(111)

    if plot_channels is not None:
        voltages = {
            ch: voltage
            for ch, voltage in voltages.items() if ch in plot_channels
        }

    for ch_name, voltage in voltages.items():
        label = 'channel {}'.format(ch_name)
        if stepped:
            line, = axes.step(times, voltage, **{
                **dict(where='post', label=label),
                **kwargs
            })
        else:
            line, = axes.plot(times, voltage, **{
                **dict(label=label),
                **kwargs
            })
        legend_handles.append(line)

    if plot_measurements:
        measurement_dict = dict()
        for name, begin, length in measurements:
            if name in plot_measurements:
                measurement_dict.setdefault(name, []).append(
                    (begin, begin + length))

        color_map = plt.cm.get_cmap('plasma')
        meas_colors = {
            name: color_map(i / len(measurement_dict))
            for i, name in enumerate(measurement_dict.keys())
        }
        for name, begin_end_list in measurement_dict.items():
            for begin, end in begin_end_list:
                poly = axes.axvspan(begin,
                                    end,
                                    alpha=0.2,
                                    label=name,
                                    edgecolor='black',
                                    facecolor=meas_colors[name])
            legend_handles.append(poly)

    axes.legend(handles=legend_handles)

    max_voltage = max(
        (max(channel, default=0) for channel in voltages.values()), default=0)
    min_voltage = min(
        (min(channel, default=0) for channel in voltages.values()), default=0)

    # add some margins in the presentation
    axes.set_xlim(-0.5 + time_slice[0], time_slice[1] + 0.5)
    voltage_difference = max_voltage - min_voltage
    if voltage_difference > 0:
        axes.set_ylim(min_voltage - 0.1 * voltage_difference,
                      max_voltage + 0.1 * voltage_difference)
    axes.set_xlabel('Time (ns)')
    axes.set_ylabel('Voltage (a.u.)')

    if pulse.identifier:
        axes.set_title(pulse.identifier)

    if show:
        axes.get_figure().show()
    return axes.get_figure()
예제 #4
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)
예제 #5
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)
 def build_program_with_create_program(self, pulse_template: PulseTemplate,
                                       **kwargs):
     return pulse_template.create_program(**kwargs)